diff options
Diffstat (limited to 'drivers')
1087 files changed, 30135 insertions, 16277 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index b3138fbb46a4..0a0a90f52d26 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig" source "drivers/spi/Kconfig" +source "drivers/spmi/Kconfig" + source "drivers/hsi/Kconfig" source "drivers/pps/Kconfig" @@ -170,4 +172,6 @@ source "drivers/phy/Kconfig" source "drivers/powercap/Kconfig" +source "drivers/mcb/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 8e3b8b06c0b2..e3ced91b1784 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_ATA) += ata/ obj-$(CONFIG_TARGET_CORE) += target/ obj-$(CONFIG_MTD) += mtd/ obj-$(CONFIG_SPI) += spi/ +obj-$(CONFIG_SPMI) += spmi/ obj-y += hsi/ obj-y += net/ obj-$(CONFIG_ATM) += atm/ @@ -155,3 +156,4 @@ obj-$(CONFIG_IPACK_BUS) += ipack/ obj-$(CONFIG_NTB) += ntb/ obj-$(CONFIG_FMC) += fmc/ obj-$(CONFIG_POWERCAP) += powercap/ +obj-$(CONFIG_MCB) += mcb/ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 4770de5707b9..c205653e9644 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -43,19 +43,6 @@ config ACPI_SLEEP depends on SUSPEND || HIBERNATION default y -config ACPI_PROCFS - bool "Deprecated /proc/acpi files" - depends on PROC_FS - help - For backwards compatibility, this option allows - deprecated /proc/acpi/ files to exist, even when - they have been replaced by functions in /sys. - - This option has no effect on /proc/acpi/ files - and functions which do not yet exist in /sys. - - Say N to delete /proc/acpi/ files that have moved to /sys/ - config ACPI_EC_DEBUGFS tristate "EC read/write access through /sys/kernel/debug/ec" default n @@ -115,7 +102,7 @@ config ACPI_BUTTON config ACPI_VIDEO tristate "Video" - depends on X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL + depends on X86 && BACKLIGHT_CLASS_DEVICE depends on INPUT select THERMAL help @@ -343,6 +330,19 @@ config ACPI_BGRT data from the firmware boot splash. It will appear under /sys/firmware/acpi/bgrt/ . +config ACPI_REDUCED_HARDWARE_ONLY + bool "Hardware-reduced ACPI support only" if EXPERT + def_bool n + depends on ACPI + help + This config item changes the way the ACPI code is built. When this + option is selected, the kernel will use a specialized version of + ACPICA that ONLY supports the ACPI "reduced hardware" mode. The + resulting kernel will be smaller but it will also be restricted to + running in ACPI reduced hardware mode ONLY. + + If you are unsure what to do, do not enable this option. + source "drivers/acpi/apei/Kconfig" config ACPI_EXTLOG diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 6f190bc2b8b7..2c01c1da29ce 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -33,6 +33,7 @@ #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/acpi.h> +#include "battery.h" #define PREFIX "ACPI: " @@ -57,6 +58,7 @@ struct acpi_ac { struct power_supply charger; struct platform_device *pdev; unsigned long long state; + struct notifier_block battery_nb; }; #define to_acpi_ac(x) container_of(x, struct acpi_ac, charger) @@ -152,6 +154,26 @@ static void acpi_ac_notify_handler(acpi_handle handle, u32 event, void *data) return; } +static int acpi_ac_battery_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct acpi_ac *ac = container_of(nb, struct acpi_ac, battery_nb); + struct acpi_bus_event *event = (struct acpi_bus_event *)data; + + /* + * On HP Pavilion dv6-6179er AC status notifications aren't triggered + * when adapter is plugged/unplugged. However, battery status + * notifcations are triggered when battery starts charging or + * discharging. Re-reading AC status triggers lost AC notifications, + * if AC status has changed. + */ + if (strcmp(event->device_class, ACPI_BATTERY_CLASS) == 0 && + event->type == ACPI_BATTERY_NOTIFY_STATUS) + acpi_ac_get_state(ac); + + return NOTIFY_OK; +} + static int thinkpad_e530_quirk(const struct dmi_system_id *d) { ac_sleep_before_get_state_ms = 1000; @@ -215,6 +237,8 @@ static int acpi_ac_probe(struct platform_device *pdev) acpi_device_name(adev), acpi_device_bid(adev), ac->state ? "on-line" : "off-line"); + ac->battery_nb.notifier_call = acpi_ac_battery_notify; + register_acpi_notifier(&ac->battery_nb); end: if (result) kfree(ac); @@ -261,6 +285,7 @@ static int acpi_ac_remove(struct platform_device *pdev) ac = platform_get_drvdata(pdev); if (ac->charger.dev) power_supply_unregister(&ac->charger); + unregister_acpi_notifier(&ac->battery_nb); kfree(ac); diff --git a/drivers/acpi/acpi_cmos_rtc.c b/drivers/acpi/acpi_cmos_rtc.c index 84190ed89c04..961b45d18a5d 100644 --- a/drivers/acpi/acpi_cmos_rtc.c +++ b/drivers/acpi/acpi_cmos_rtc.c @@ -18,8 +18,6 @@ #include "internal.h" -#define PREFIX "ACPI: " - ACPI_MODULE_NAME("cmos rtc"); static const struct acpi_device_id acpi_cmos_rtc_ids[] = { diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 6745fe137b9e..69e29f409d4c 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -33,6 +33,13 @@ ACPI_MODULE_NAME("acpi_lpss"); #define LPSS_GENERAL_UART_RTS_OVRD BIT(3) #define LPSS_SW_LTR 0x10 #define LPSS_AUTO_LTR 0x14 +#define LPSS_LTR_SNOOP_REQ BIT(15) +#define LPSS_LTR_SNOOP_MASK 0x0000FFFF +#define LPSS_LTR_SNOOP_LAT_1US 0x800 +#define LPSS_LTR_SNOOP_LAT_32US 0xC00 +#define LPSS_LTR_SNOOP_LAT_SHIFT 5 +#define LPSS_LTR_SNOOP_LAT_CUTOFF 3000 +#define LPSS_LTR_MAX_VAL 0x3FF #define LPSS_TX_INT 0x20 #define LPSS_TX_INT_MASK BIT(1) @@ -102,6 +109,16 @@ static struct lpss_device_desc lpt_sdio_dev_desc = { .ltr_required = true, }; +static struct lpss_shared_clock pwm_clock = { + .name = "pwm_clk", + .rate = 25000000, +}; + +static struct lpss_device_desc byt_pwm_dev_desc = { + .clk_required = true, + .shared_clock = &pwm_clock, +}; + static struct lpss_shared_clock uart_clock = { .name = "uart_clk", .rate = 44236800, @@ -157,6 +174,7 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = { { "INT33C7", }, /* BayTrail LPSS devices */ + { "80860F09", (unsigned long)&byt_pwm_dev_desc }, { "80860F0A", (unsigned long)&byt_uart_dev_desc }, { "80860F0E", (unsigned long)&byt_spi_dev_desc }, { "80860F14", (unsigned long)&byt_sdio_dev_desc }, @@ -315,6 +333,17 @@ static int acpi_lpss_create_device(struct acpi_device *adev, return ret; } +static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg) +{ + return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg); +} + +static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata, + unsigned int reg) +{ + writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg); +} + static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) { struct acpi_device *adev; @@ -336,7 +365,7 @@ static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) ret = -ENODEV; goto out; } - *val = readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg); + *val = __lpss_reg_read(pdata, reg); out: spin_unlock_irqrestore(&dev->power.lock, flags); @@ -389,6 +418,37 @@ static struct attribute_group lpss_attr_group = { .name = "lpss_ltr", }; +static void acpi_lpss_set_ltr(struct device *dev, s32 val) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + u32 ltr_mode, ltr_val; + + ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL); + if (val < 0) { + if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) { + ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW; + __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL); + } + return; + } + ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK; + if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) { + ltr_val |= LPSS_LTR_SNOOP_LAT_32US; + val = LPSS_LTR_MAX_VAL; + } else if (val > LPSS_LTR_MAX_VAL) { + ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ; + val >>= LPSS_LTR_SNOOP_LAT_SHIFT; + } else { + ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ; + } + ltr_val |= val; + __lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR); + if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) { + ltr_mode |= LPSS_GENERAL_LTR_MODE_SW; + __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL); + } +} + static int acpi_lpss_platform_notify(struct notifier_block *nb, unsigned long action, void *data) { @@ -426,9 +486,29 @@ static struct notifier_block acpi_lpss_nb = { .notifier_call = acpi_lpss_platform_notify, }; +static void acpi_lpss_bind(struct device *dev) +{ + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required) + return; + + if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) + dev->power.set_latency_tolerance = acpi_lpss_set_ltr; + else + dev_err(dev, "MMIO size insufficient to access LTR\n"); +} + +static void acpi_lpss_unbind(struct device *dev) +{ + dev->power.set_latency_tolerance = NULL; +} + static struct acpi_scan_handler lpss_handler = { .ids = acpi_lpss_device_ids, .attach = acpi_lpss_create_device, + .bind = acpi_lpss_bind, + .unbind = acpi_lpss_unbind, }; void __init acpi_lpss_init(void) diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index df96a0fe4890..37d73024b82e 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -408,28 +408,14 @@ static int acpi_pad_pur(acpi_handle handle) return num; } -/* Notify firmware how many CPUs are idle */ -static void acpi_pad_ost(acpi_handle handle, int stat, - uint32_t idle_cpus) -{ - union acpi_object params[3] = { - {.type = ACPI_TYPE_INTEGER,}, - {.type = ACPI_TYPE_INTEGER,}, - {.type = ACPI_TYPE_BUFFER,}, - }; - struct acpi_object_list arg_list = {3, params}; - - params[0].integer.value = ACPI_PROCESSOR_AGGREGATOR_NOTIFY; - params[1].integer.value = stat; - params[2].buffer.length = 4; - params[2].buffer.pointer = (void *)&idle_cpus; - acpi_evaluate_object(handle, "_OST", &arg_list, NULL); -} - static void acpi_pad_handle_notify(acpi_handle handle) { int num_cpus; uint32_t idle_cpus; + struct acpi_buffer param = { + .length = 4, + .pointer = (void *)&idle_cpus, + }; mutex_lock(&isolated_cpus_lock); num_cpus = acpi_pad_pur(handle); @@ -439,7 +425,7 @@ static void acpi_pad_handle_notify(acpi_handle handle) } acpi_pad_idle_cpus(num_cpus); idle_cpus = acpi_pad_idle_cpus_num(); - acpi_pad_ost(handle, 0, idle_cpus); + acpi_evaluate_ost(handle, ACPI_PROCESSOR_AGGREGATOR_NOTIFY, 0, ¶m); mutex_unlock(&isolated_cpus_lock); } diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 438304086ff1..b7ed86a20427 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -122,6 +122,8 @@ acpi-y += \ rsaddr.o \ rscalc.o \ rscreate.o \ + rsdump.o \ + rsdumpinfo.o \ rsinfo.o \ rsio.o \ rsirq.o \ @@ -132,8 +134,6 @@ acpi-y += \ rsutils.o \ rsxface.o -acpi-$(ACPI_FUTURE_USAGE) += rsdump.o rsdumpinfo.o - acpi-y += \ tbfadt.o \ tbfind.o \ diff --git a/drivers/acpi/acpica/accommon.h b/drivers/acpi/acpica/accommon.h index 8a6c4a0d22db..6f1c616910ac 100644 --- a/drivers/acpi/acpica/accommon.h +++ b/drivers/acpi/acpica/accommon.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 2bf3ca2b8a7a..68a91eb0fa48 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -115,6 +115,8 @@ ACPI_HW_DEPENDENT_RETURN_VOID(void char *block_arg)) ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_db_generate_sci(void)) +void acpi_db_execute_test(char *type_arg); + /* * dbconvert - miscellaneous conversion routines */ diff --git a/drivers/acpi/acpica/acdispat.h b/drivers/acpi/acpica/acdispat.h index 427db72a6302..5b472c43c31d 100644 --- a/drivers/acpi/acpica/acdispat.h +++ b/drivers/acpi/acpica/acdispat.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 0fb0adf435d6..68ec61fff188 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 4ed1aa384df2..8f40bb972ae3 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -51,11 +51,19 @@ * to simplify maintenance of the code. */ #ifdef DEFINE_ACPI_GLOBALS -#define ACPI_EXTERN -#define ACPI_INIT_GLOBAL(a,b) a=b +#define ACPI_GLOBAL(type,name) \ + extern type name; \ + type name + +#define ACPI_INIT_GLOBAL(type,name,value) \ + type name=value + #else -#define ACPI_EXTERN extern -#define ACPI_INIT_GLOBAL(a,b) a +#define ACPI_GLOBAL(type,name) \ + extern type name + +#define ACPI_INIT_GLOBAL(type,name,value) \ + extern type name #endif #ifdef DEFINE_ACPI_GLOBALS @@ -82,7 +90,7 @@ * 5) Allow unresolved references (invalid target name) in package objects * 6) Enable warning messages for behavior that is not ACPI spec compliant */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_enable_interpreter_slack, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_enable_interpreter_slack, FALSE); /* * Automatically serialize ALL control methods? Default is FALSE, meaning @@ -90,25 +98,25 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_enable_interpreter_slack, FALSE); * Only change this if the ASL code is poorly written and cannot handle * reentrancy even though methods are marked "NotSerialized". */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_all_methods_serialized, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_all_methods_serialized, FALSE); /* * Create the predefined _OSI method in the namespace? Default is TRUE * because ACPI CA is fully compatible with other ACPI implementations. * Changing this will revert ACPI CA (and machine ASL) to pre-OSI behavior. */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_create_osi_method, TRUE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_create_osi_method, TRUE); /* * Optionally use default values for the ACPI register widths. Set this to * TRUE to use the defaults, if an FADT contains incorrect widths/lengths. */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_use_default_register_widths, TRUE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_use_default_register_widths, TRUE); /* * Optionally enable output from the AML Debug Object. */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_enable_aml_debug_object, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_enable_aml_debug_object, FALSE); /* * Optionally copy the entire DSDT to local memory (instead of simply @@ -116,7 +124,7 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_enable_aml_debug_object, FALSE); * DSDT, creating the need for this option. Default is FALSE, do not copy * the DSDT. */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_copy_dsdt_locally, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_copy_dsdt_locally, FALSE); /* * Optionally ignore an XSDT if present and use the RSDT instead. @@ -124,7 +132,7 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_copy_dsdt_locally, FALSE); * of the RSDT, the XSDT has been found to be corrupt or ill-formed on * some machines. Default behavior is to use the XSDT if present. */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_do_not_use_xsdt, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE); /* * Optionally use 32-bit FADT addresses if and when there is a conflict @@ -134,7 +142,7 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_do_not_use_xsdt, FALSE); * some machines have been found to have a corrupted non-zero 64-bit * address. Default is FALSE, do not favor the 32-bit addresses. */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_use32_bit_fadt_addresses, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_use32_bit_fadt_addresses, FALSE); /* * Optionally truncate I/O addresses to 16 bits. Provides compatibility @@ -142,47 +150,28 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_use32_bit_fadt_addresses, FALSE); * this value is set to TRUE if any Windows OSI strings have been * requested by the BIOS. */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_truncate_io_addresses, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_truncate_io_addresses, FALSE); /* * Disable runtime checking and repair of values returned by control methods. * Use only if the repair is causing a problem on a particular machine. */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_disable_auto_repair, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_auto_repair, FALSE); /* * Optionally do not load any SSDTs from the RSDT/XSDT during initialization. * This can be useful for debugging ACPI problems on some machines. */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_disable_ssdt_table_load, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_disable_ssdt_table_load, FALSE); /* * We keep track of the latest version of Windows that has been requested by * the BIOS. */ -u8 ACPI_INIT_GLOBAL(acpi_gbl_osi_data, 0); - -/* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */ - -struct acpi_table_fadt acpi_gbl_FADT; -u32 acpi_current_gpe_count; -u32 acpi_gbl_trace_flags; -acpi_name acpi_gbl_trace_method_name; -u8 acpi_gbl_system_awake_and_running; - -/* - * ACPI 5.0 introduces the concept of a "reduced hardware platform", meaning - * that the ACPI hardware is no longer required. A flag in the FADT indicates - * a reduced HW machine, and that flag is duplicated here for convenience. - */ -u8 acpi_gbl_reduced_hardware; +ACPI_INIT_GLOBAL(u8, acpi_gbl_osi_data, 0); #endif /* DEFINE_ACPI_GLOBALS */ -/* Do not disassemble buffers to resource descriptors */ - -ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_no_resource_disassembly, FALSE); - /***************************************************************************** * * ACPI Table globals @@ -190,37 +179,36 @@ ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_no_resource_disassembly, FALSE); ****************************************************************************/ /* - * acpi_gbl_root_table_list is the master list of ACPI tables that were - * found in the RSDT/XSDT. + * Master list of all ACPI tables that were found in the RSDT/XSDT. */ -ACPI_EXTERN struct acpi_table_list acpi_gbl_root_table_list; +ACPI_GLOBAL(struct acpi_table_list, acpi_gbl_root_table_list); + +/* DSDT information. Used to check for DSDT corruption */ + +ACPI_GLOBAL(struct acpi_table_header *, acpi_gbl_DSDT); +ACPI_GLOBAL(struct acpi_table_header, acpi_gbl_original_dsdt_header); #if (!ACPI_REDUCED_HARDWARE) -ACPI_EXTERN struct acpi_table_facs *acpi_gbl_FACS; +ACPI_GLOBAL(struct acpi_table_facs *, acpi_gbl_FACS); #endif /* !ACPI_REDUCED_HARDWARE */ /* These addresses are calculated from the FADT Event Block addresses */ -ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1a_status; -ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1a_enable; - -ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1b_status; -ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1b_enable; +ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1a_status); +ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1a_enable); -/* DSDT information. Used to check for DSDT corruption */ - -ACPI_EXTERN struct acpi_table_header *acpi_gbl_DSDT; -ACPI_EXTERN struct acpi_table_header acpi_gbl_original_dsdt_header; +ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1b_status); +ACPI_GLOBAL(struct acpi_generic_address, acpi_gbl_xpm1b_enable); /* - * Handle both ACPI 1.0 and ACPI 2.0 Integer widths. The integer width is + * Handle both ACPI 1.0 and ACPI 2.0+ Integer widths. The integer width is * determined by the revision of the DSDT: If the DSDT revision is less than * 2, use only the lower 32 bits of the internal 64-bit Integer. */ -ACPI_EXTERN u8 acpi_gbl_integer_bit_width; -ACPI_EXTERN u8 acpi_gbl_integer_byte_width; -ACPI_EXTERN u8 acpi_gbl_integer_nybble_width; +ACPI_GLOBAL(u8, acpi_gbl_integer_bit_width); +ACPI_GLOBAL(u8, acpi_gbl_integer_byte_width); +ACPI_GLOBAL(u8, acpi_gbl_integer_nybble_width); /***************************************************************************** * @@ -233,36 +221,36 @@ ACPI_EXTERN u8 acpi_gbl_integer_nybble_width; * actual OS mutex handles, indexed by the local ACPI_MUTEX_HANDLEs. * (The table maps local handles to the real OS handles) */ -ACPI_EXTERN struct acpi_mutex_info acpi_gbl_mutex_info[ACPI_NUM_MUTEX]; +ACPI_GLOBAL(struct acpi_mutex_info, acpi_gbl_mutex_info[ACPI_NUM_MUTEX]); /* * Global lock mutex is an actual AML mutex object * Global lock semaphore works in conjunction with the actual global lock * Global lock spinlock is used for "pending" handshake */ -ACPI_EXTERN union acpi_operand_object *acpi_gbl_global_lock_mutex; -ACPI_EXTERN acpi_semaphore acpi_gbl_global_lock_semaphore; -ACPI_EXTERN acpi_spinlock acpi_gbl_global_lock_pending_lock; -ACPI_EXTERN u16 acpi_gbl_global_lock_handle; -ACPI_EXTERN u8 acpi_gbl_global_lock_acquired; -ACPI_EXTERN u8 acpi_gbl_global_lock_present; -ACPI_EXTERN u8 acpi_gbl_global_lock_pending; +ACPI_GLOBAL(union acpi_operand_object *, acpi_gbl_global_lock_mutex); +ACPI_GLOBAL(acpi_semaphore, acpi_gbl_global_lock_semaphore); +ACPI_GLOBAL(acpi_spinlock, acpi_gbl_global_lock_pending_lock); +ACPI_GLOBAL(u16, acpi_gbl_global_lock_handle); +ACPI_GLOBAL(u8, acpi_gbl_global_lock_acquired); +ACPI_GLOBAL(u8, acpi_gbl_global_lock_present); +ACPI_GLOBAL(u8, acpi_gbl_global_lock_pending); /* * Spinlocks are used for interfaces that can be possibly called at * interrupt level */ -ACPI_EXTERN acpi_spinlock acpi_gbl_gpe_lock; /* For GPE data structs and registers */ -ACPI_EXTERN acpi_spinlock acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */ -ACPI_EXTERN acpi_spinlock acpi_gbl_reference_count_lock; +ACPI_GLOBAL(acpi_spinlock, acpi_gbl_gpe_lock); /* For GPE data structs and registers */ +ACPI_GLOBAL(acpi_spinlock, acpi_gbl_hardware_lock); /* For ACPI H/W except GPE registers */ +ACPI_GLOBAL(acpi_spinlock, acpi_gbl_reference_count_lock); /* Mutex for _OSI support */ -ACPI_EXTERN acpi_mutex acpi_gbl_osi_mutex; +ACPI_GLOBAL(acpi_mutex, acpi_gbl_osi_mutex); /* Reader/Writer lock is used for namespace walk and dynamic table unload */ -ACPI_EXTERN struct acpi_rw_lock acpi_gbl_namespace_rw_lock; +ACPI_GLOBAL(struct acpi_rw_lock, acpi_gbl_namespace_rw_lock); /***************************************************************************** * @@ -272,70 +260,69 @@ ACPI_EXTERN struct acpi_rw_lock acpi_gbl_namespace_rw_lock; /* Object caches */ -ACPI_EXTERN acpi_cache_t *acpi_gbl_namespace_cache; -ACPI_EXTERN acpi_cache_t *acpi_gbl_state_cache; -ACPI_EXTERN acpi_cache_t *acpi_gbl_ps_node_cache; -ACPI_EXTERN acpi_cache_t *acpi_gbl_ps_node_ext_cache; -ACPI_EXTERN acpi_cache_t *acpi_gbl_operand_cache; +ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_namespace_cache); +ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_state_cache); +ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_ps_node_cache); +ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_ps_node_ext_cache); +ACPI_GLOBAL(acpi_cache_t *, acpi_gbl_operand_cache); + +/* System */ + +ACPI_INIT_GLOBAL(u32, acpi_gbl_startup_flags, 0); +ACPI_INIT_GLOBAL(u8, acpi_gbl_shutdown, TRUE); /* Global handlers */ -ACPI_EXTERN struct acpi_global_notify_handler acpi_gbl_global_notify[2]; -ACPI_EXTERN acpi_exception_handler acpi_gbl_exception_handler; -ACPI_EXTERN acpi_init_handler acpi_gbl_init_handler; -ACPI_EXTERN acpi_table_handler acpi_gbl_table_handler; -ACPI_EXTERN void *acpi_gbl_table_handler_context; -ACPI_EXTERN struct acpi_walk_state *acpi_gbl_breakpoint_walk; -ACPI_EXTERN acpi_interface_handler acpi_gbl_interface_handler; -ACPI_EXTERN struct acpi_sci_handler_info *acpi_gbl_sci_handler_list; +ACPI_GLOBAL(struct acpi_global_notify_handler, acpi_gbl_global_notify[2]); +ACPI_GLOBAL(acpi_exception_handler, acpi_gbl_exception_handler); +ACPI_GLOBAL(acpi_init_handler, acpi_gbl_init_handler); +ACPI_GLOBAL(acpi_table_handler, acpi_gbl_table_handler); +ACPI_GLOBAL(void *, acpi_gbl_table_handler_context); +ACPI_GLOBAL(struct acpi_walk_state *, acpi_gbl_breakpoint_walk); +ACPI_GLOBAL(acpi_interface_handler, acpi_gbl_interface_handler); +ACPI_GLOBAL(struct acpi_sci_handler_info *, acpi_gbl_sci_handler_list); /* Owner ID support */ -ACPI_EXTERN u32 acpi_gbl_owner_id_mask[ACPI_NUM_OWNERID_MASKS]; -ACPI_EXTERN u8 acpi_gbl_last_owner_id_index; -ACPI_EXTERN u8 acpi_gbl_next_owner_id_offset; +ACPI_GLOBAL(u32, acpi_gbl_owner_id_mask[ACPI_NUM_OWNERID_MASKS]); +ACPI_GLOBAL(u8, acpi_gbl_last_owner_id_index); +ACPI_GLOBAL(u8, acpi_gbl_next_owner_id_offset); /* Initialization sequencing */ -ACPI_EXTERN u8 acpi_gbl_reg_methods_executed; +ACPI_GLOBAL(u8, acpi_gbl_reg_methods_executed); /* Misc */ -ACPI_EXTERN u32 acpi_gbl_original_mode; -ACPI_EXTERN u32 acpi_gbl_rsdp_original_location; -ACPI_EXTERN u32 acpi_gbl_ns_lookup_count; -ACPI_EXTERN u32 acpi_gbl_ps_find_count; -ACPI_EXTERN u16 acpi_gbl_pm1_enable_register_save; -ACPI_EXTERN u8 acpi_gbl_debugger_configuration; -ACPI_EXTERN u8 acpi_gbl_step_to_next_call; -ACPI_EXTERN u8 acpi_gbl_acpi_hardware_present; -ACPI_EXTERN u8 acpi_gbl_events_initialized; -ACPI_EXTERN struct acpi_interface_info *acpi_gbl_supported_interfaces; -ACPI_EXTERN struct acpi_address_range - *acpi_gbl_address_range_list[ACPI_ADDRESS_RANGE_MAX]; - -#ifndef DEFINE_ACPI_GLOBALS - -/* Other miscellaneous */ - -extern u8 acpi_gbl_shutdown; -extern u32 acpi_gbl_startup_flags; +ACPI_GLOBAL(u32, acpi_gbl_original_mode); +ACPI_GLOBAL(u32, acpi_gbl_rsdp_original_location); +ACPI_GLOBAL(u32, acpi_gbl_ns_lookup_count); +ACPI_GLOBAL(u32, acpi_gbl_ps_find_count); +ACPI_GLOBAL(u16, acpi_gbl_pm1_enable_register_save); +ACPI_GLOBAL(u8, acpi_gbl_debugger_configuration); +ACPI_GLOBAL(u8, acpi_gbl_step_to_next_call); +ACPI_GLOBAL(u8, acpi_gbl_acpi_hardware_present); +ACPI_GLOBAL(u8, acpi_gbl_events_initialized); +ACPI_GLOBAL(struct acpi_interface_info *, acpi_gbl_supported_interfaces); +ACPI_GLOBAL(struct acpi_address_range *, + acpi_gbl_address_range_list[ACPI_ADDRESS_RANGE_MAX]); + +/* Other miscellaneous, declared and initialized in utglobal */ + extern const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT]; extern const char *acpi_gbl_lowest_dstate_names[ACPI_NUM_sx_w_METHODS]; extern const char *acpi_gbl_highest_dstate_names[ACPI_NUM_sx_d_METHODS]; -extern const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES]; extern const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS]; - -#endif +extern const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES]; #ifdef ACPI_DBG_TRACK_ALLOCATIONS -/* Lists for tracking memory allocations */ +/* Lists for tracking memory allocations (debug only) */ -ACPI_EXTERN struct acpi_memory_list *acpi_gbl_global_list; -ACPI_EXTERN struct acpi_memory_list *acpi_gbl_ns_node_list; -ACPI_EXTERN u8 acpi_gbl_display_final_mem_stats; -ACPI_EXTERN u8 acpi_gbl_disable_mem_tracking; +ACPI_GLOBAL(struct acpi_memory_list *, acpi_gbl_global_list); +ACPI_GLOBAL(struct acpi_memory_list *, acpi_gbl_ns_node_list); +ACPI_GLOBAL(u8, acpi_gbl_display_final_mem_stats); +ACPI_GLOBAL(u8, acpi_gbl_disable_mem_tracking); #endif /***************************************************************************** @@ -350,22 +337,23 @@ ACPI_EXTERN u8 acpi_gbl_disable_mem_tracking; #define NUM_PREDEFINED_NAMES 9 #endif -ACPI_EXTERN struct acpi_namespace_node acpi_gbl_root_node_struct; -ACPI_EXTERN struct acpi_namespace_node *acpi_gbl_root_node; -ACPI_EXTERN struct acpi_namespace_node *acpi_gbl_fadt_gpe_device; -ACPI_EXTERN union acpi_operand_object *acpi_gbl_module_code_list; +ACPI_GLOBAL(struct acpi_namespace_node, acpi_gbl_root_node_struct); +ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_root_node); +ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_fadt_gpe_device); +ACPI_GLOBAL(union acpi_operand_object *, acpi_gbl_module_code_list); extern const u8 acpi_gbl_ns_properties[ACPI_NUM_NS_TYPES]; extern const struct acpi_predefined_names acpi_gbl_pre_defined_names[NUM_PREDEFINED_NAMES]; #ifdef ACPI_DEBUG_OUTPUT -ACPI_EXTERN u32 acpi_gbl_current_node_count; -ACPI_EXTERN u32 acpi_gbl_current_node_size; -ACPI_EXTERN u32 acpi_gbl_max_concurrent_node_count; -ACPI_EXTERN acpi_size *acpi_gbl_entry_stack_pointer; -ACPI_EXTERN acpi_size *acpi_gbl_lowest_stack_pointer; -ACPI_EXTERN u32 acpi_gbl_deepest_nesting; +ACPI_GLOBAL(u32, acpi_gbl_current_node_count); +ACPI_GLOBAL(u32, acpi_gbl_current_node_size); +ACPI_GLOBAL(u32, acpi_gbl_max_concurrent_node_count); +ACPI_GLOBAL(acpi_size *, acpi_gbl_entry_stack_pointer); +ACPI_GLOBAL(acpi_size *, acpi_gbl_lowest_stack_pointer); +ACPI_GLOBAL(u32, acpi_gbl_deepest_nesting); +ACPI_INIT_GLOBAL(u32, acpi_gbl_nesting_level, 0); #endif /***************************************************************************** @@ -374,11 +362,11 @@ ACPI_EXTERN u32 acpi_gbl_deepest_nesting; * ****************************************************************************/ -ACPI_EXTERN struct acpi_thread_state *acpi_gbl_current_walk_list; +ACPI_GLOBAL(struct acpi_thread_state *, acpi_gbl_current_walk_list); /* Control method single step flag */ -ACPI_EXTERN u8 acpi_gbl_cm_single_step; +ACPI_GLOBAL(u8, acpi_gbl_cm_single_step); /***************************************************************************** * @@ -388,8 +376,9 @@ ACPI_EXTERN u8 acpi_gbl_cm_single_step; extern struct acpi_bit_register_info acpi_gbl_bit_register_info[ACPI_NUM_BITREG]; -ACPI_EXTERN u8 acpi_gbl_sleep_type_a; -ACPI_EXTERN u8 acpi_gbl_sleep_type_b; + +ACPI_GLOBAL(u8, acpi_gbl_sleep_type_a); +ACPI_GLOBAL(u8, acpi_gbl_sleep_type_b); /***************************************************************************** * @@ -399,14 +388,15 @@ ACPI_EXTERN u8 acpi_gbl_sleep_type_b; #if (!ACPI_REDUCED_HARDWARE) -ACPI_EXTERN u8 acpi_gbl_all_gpes_initialized; -ACPI_EXTERN struct acpi_gpe_xrupt_info *acpi_gbl_gpe_xrupt_list_head; -ACPI_EXTERN struct acpi_gpe_block_info - *acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS]; -ACPI_EXTERN acpi_gbl_event_handler acpi_gbl_global_event_handler; -ACPI_EXTERN void *acpi_gbl_global_event_handler_context; -ACPI_EXTERN struct acpi_fixed_event_handler - acpi_gbl_fixed_event_handlers[ACPI_NUM_FIXED_EVENTS]; +ACPI_GLOBAL(u8, acpi_gbl_all_gpes_initialized); +ACPI_GLOBAL(struct acpi_gpe_xrupt_info *, acpi_gbl_gpe_xrupt_list_head); +ACPI_GLOBAL(struct acpi_gpe_block_info *, + acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS]); +ACPI_GLOBAL(acpi_gbl_event_handler, acpi_gbl_global_event_handler); +ACPI_GLOBAL(void *, acpi_gbl_global_event_handler_context); +ACPI_GLOBAL(struct acpi_fixed_event_handler, + acpi_gbl_fixed_event_handlers[ACPI_NUM_FIXED_EVENTS]); + extern struct acpi_fixed_event_info acpi_gbl_fixed_event_info[ACPI_NUM_FIXED_EVENTS]; @@ -418,23 +408,19 @@ extern struct acpi_fixed_event_info * ****************************************************************************/ -/* Procedure nesting level for debug output */ - -extern u32 acpi_gbl_nesting_level; - /* Event counters */ -ACPI_EXTERN u32 acpi_method_count; -ACPI_EXTERN u32 acpi_gpe_count; -ACPI_EXTERN u32 acpi_sci_count; -ACPI_EXTERN u32 acpi_fixed_event_count[ACPI_NUM_FIXED_EVENTS]; +ACPI_GLOBAL(u32, acpi_method_count); +ACPI_GLOBAL(u32, acpi_gpe_count); +ACPI_GLOBAL(u32, acpi_sci_count); +ACPI_GLOBAL(u32, acpi_fixed_event_count[ACPI_NUM_FIXED_EVENTS]); /* Support for dynamic control method tracing mechanism */ -ACPI_EXTERN u32 acpi_gbl_original_dbg_level; -ACPI_EXTERN u32 acpi_gbl_original_dbg_layer; -ACPI_EXTERN u32 acpi_gbl_trace_dbg_level; -ACPI_EXTERN u32 acpi_gbl_trace_dbg_layer; +ACPI_GLOBAL(u32, acpi_gbl_original_dbg_level); +ACPI_GLOBAL(u32, acpi_gbl_original_dbg_layer); +ACPI_GLOBAL(u32, acpi_gbl_trace_dbg_level); +ACPI_GLOBAL(u32, acpi_gbl_trace_dbg_layer); /***************************************************************************** * @@ -442,61 +428,64 @@ ACPI_EXTERN u32 acpi_gbl_trace_dbg_layer; * ****************************************************************************/ -ACPI_EXTERN u8 acpi_gbl_db_output_flags; +ACPI_GLOBAL(u8, acpi_gbl_db_output_flags); #ifdef ACPI_DISASSEMBLER -ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_ignore_noop_operator, FALSE); +/* Do not disassemble buffers to resource descriptors */ + +ACPI_INIT_GLOBAL(u8, acpi_gbl_no_resource_disassembly, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_ignore_noop_operator, FALSE); -ACPI_EXTERN u8 acpi_gbl_db_opt_disasm; -ACPI_EXTERN u8 acpi_gbl_db_opt_verbose; -ACPI_EXTERN u8 acpi_gbl_num_external_methods; -ACPI_EXTERN u32 acpi_gbl_resolved_external_methods; -ACPI_EXTERN struct acpi_external_list *acpi_gbl_external_list; -ACPI_EXTERN struct acpi_external_file *acpi_gbl_external_file_list; +ACPI_GLOBAL(u8, acpi_gbl_db_opt_disasm); +ACPI_GLOBAL(u8, acpi_gbl_db_opt_verbose); +ACPI_GLOBAL(u8, acpi_gbl_num_external_methods); +ACPI_GLOBAL(u32, acpi_gbl_resolved_external_methods); +ACPI_GLOBAL(struct acpi_external_list *, acpi_gbl_external_list); +ACPI_GLOBAL(struct acpi_external_file *, acpi_gbl_external_file_list); #endif #ifdef ACPI_DEBUGGER -extern u8 acpi_gbl_method_executing; -extern u8 acpi_gbl_abort_method; -extern u8 acpi_gbl_db_terminate_threads; +ACPI_INIT_GLOBAL(u8, acpi_gbl_db_terminate_threads, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_abort_method, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_method_executing, FALSE); -ACPI_EXTERN u8 acpi_gbl_db_opt_tables; -ACPI_EXTERN u8 acpi_gbl_db_opt_stats; -ACPI_EXTERN u8 acpi_gbl_db_opt_ini_methods; -ACPI_EXTERN u8 acpi_gbl_db_opt_no_region_support; -ACPI_EXTERN u8 acpi_gbl_db_output_to_file; -ACPI_EXTERN char *acpi_gbl_db_buffer; -ACPI_EXTERN char *acpi_gbl_db_filename; -ACPI_EXTERN u32 acpi_gbl_db_debug_level; -ACPI_EXTERN u32 acpi_gbl_db_console_debug_level; -ACPI_EXTERN struct acpi_namespace_node *acpi_gbl_db_scope_node; +ACPI_GLOBAL(u8, acpi_gbl_db_opt_tables); +ACPI_GLOBAL(u8, acpi_gbl_db_opt_stats); +ACPI_GLOBAL(u8, acpi_gbl_db_opt_ini_methods); +ACPI_GLOBAL(u8, acpi_gbl_db_opt_no_region_support); +ACPI_GLOBAL(u8, acpi_gbl_db_output_to_file); +ACPI_GLOBAL(char *, acpi_gbl_db_buffer); +ACPI_GLOBAL(char *, acpi_gbl_db_filename); +ACPI_GLOBAL(u32, acpi_gbl_db_debug_level); +ACPI_GLOBAL(u32, acpi_gbl_db_console_debug_level); +ACPI_GLOBAL(struct acpi_namespace_node *, acpi_gbl_db_scope_node); -ACPI_EXTERN char *acpi_gbl_db_args[ACPI_DEBUGGER_MAX_ARGS]; -ACPI_EXTERN acpi_object_type acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]; +ACPI_GLOBAL(char *, acpi_gbl_db_args[ACPI_DEBUGGER_MAX_ARGS]); +ACPI_GLOBAL(acpi_object_type, acpi_gbl_db_arg_types[ACPI_DEBUGGER_MAX_ARGS]); /* These buffers should all be the same size */ -ACPI_EXTERN char acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]; -ACPI_EXTERN char acpi_gbl_db_parsed_buf[ACPI_DB_LINE_BUFFER_SIZE]; -ACPI_EXTERN char acpi_gbl_db_scope_buf[ACPI_DB_LINE_BUFFER_SIZE]; -ACPI_EXTERN char acpi_gbl_db_debug_filename[ACPI_DB_LINE_BUFFER_SIZE]; +ACPI_GLOBAL(char, acpi_gbl_db_line_buf[ACPI_DB_LINE_BUFFER_SIZE]); +ACPI_GLOBAL(char, acpi_gbl_db_parsed_buf[ACPI_DB_LINE_BUFFER_SIZE]); +ACPI_GLOBAL(char, acpi_gbl_db_scope_buf[ACPI_DB_LINE_BUFFER_SIZE]); +ACPI_GLOBAL(char, acpi_gbl_db_debug_filename[ACPI_DB_LINE_BUFFER_SIZE]); /* * Statistic globals */ -ACPI_EXTERN u16 acpi_gbl_obj_type_count[ACPI_TYPE_NS_NODE_MAX + 1]; -ACPI_EXTERN u16 acpi_gbl_node_type_count[ACPI_TYPE_NS_NODE_MAX + 1]; -ACPI_EXTERN u16 acpi_gbl_obj_type_count_misc; -ACPI_EXTERN u16 acpi_gbl_node_type_count_misc; -ACPI_EXTERN u32 acpi_gbl_num_nodes; -ACPI_EXTERN u32 acpi_gbl_num_objects; - -ACPI_EXTERN u32 acpi_gbl_size_of_parse_tree; -ACPI_EXTERN u32 acpi_gbl_size_of_method_trees; -ACPI_EXTERN u32 acpi_gbl_size_of_node_entries; -ACPI_EXTERN u32 acpi_gbl_size_of_acpi_objects; +ACPI_GLOBAL(u16, acpi_gbl_obj_type_count[ACPI_TYPE_NS_NODE_MAX + 1]); +ACPI_GLOBAL(u16, acpi_gbl_node_type_count[ACPI_TYPE_NS_NODE_MAX + 1]); +ACPI_GLOBAL(u16, acpi_gbl_obj_type_count_misc); +ACPI_GLOBAL(u16, acpi_gbl_node_type_count_misc); +ACPI_GLOBAL(u32, acpi_gbl_num_nodes); +ACPI_GLOBAL(u32, acpi_gbl_num_objects); + +ACPI_GLOBAL(u32, acpi_gbl_size_of_parse_tree); +ACPI_GLOBAL(u32, acpi_gbl_size_of_method_trees); +ACPI_GLOBAL(u32, acpi_gbl_size_of_node_entries); +ACPI_GLOBAL(u32, acpi_gbl_size_of_acpi_objects); #endif /* ACPI_DEBUGGER */ @@ -508,7 +497,7 @@ ACPI_EXTERN u32 acpi_gbl_size_of_acpi_objects; #ifdef ACPI_APPLICATION -ACPI_FILE ACPI_INIT_GLOBAL(acpi_gbl_debug_file, NULL); +ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_debug_file, NULL); #endif /* ACPI_APPLICATION */ diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 6357e932bfd9..2ad2351a9833 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 8af8c9bdeb35..c54267748be5 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -87,6 +87,10 @@ typedef const struct acpi_exdump_info { #define ACPI_EXD_PACKAGE 11 #define ACPI_EXD_FIELD 12 #define ACPI_EXD_REFERENCE 13 +#define ACPI_EXD_LIST 14 /* Operand object list */ +#define ACPI_EXD_HDLR_LIST 15 /* Address Handler list */ +#define ACPI_EXD_RGN_LIST 16 /* Region list */ +#define ACPI_EXD_NODE 17 /* Namespace Node */ /* restore default alignment */ diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index d95ca5449ace..52a21dafb540 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 2a86c65d873b..4bceb11c7380 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -63,17 +63,21 @@ #define ACPI_SET64(ptr, val) (*ACPI_CAST64 (ptr) = (u64) (val)) /* - * printf() format helpers + * printf() format helpers. These macros are workarounds for the difficulties + * with emitting 64-bit integers and 64-bit pointers with the same code + * for both 32-bit and 64-bit hosts. */ - -/* Split 64-bit integer into two 32-bit values. Use with %8.8X%8.8X */ - #define ACPI_FORMAT_UINT64(i) ACPI_HIDWORD(i), ACPI_LODWORD(i) #if ACPI_MACHINE_WIDTH == 64 #define ACPI_FORMAT_NATIVE_UINT(i) ACPI_FORMAT_UINT64(i) +#define ACPI_FORMAT_TO_UINT(i) ACPI_FORMAT_UINT64(i) +#define ACPI_PRINTF_UINT "0x%8.8X%8.8X" + #else -#define ACPI_FORMAT_NATIVE_UINT(i) 0, (i) +#define ACPI_FORMAT_NATIVE_UINT(i) 0, (u32) (i) +#define ACPI_FORMAT_TO_UINT(i) (u32) (i) +#define ACPI_PRINTF_UINT "0x%8.8X" #endif /* diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index e6138ac4a160..ee1c040f321c 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 cc7ab6dd724e..1a4d61805ebc 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 3fc9ca7e8aa3..dda0e6affcf1 100644 --- a/drivers/acpi/acpica/acopcode.h +++ b/drivers/acpi/acpica/acopcode.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 aed319318835..6168b85463ed 100644 --- a/drivers/acpi/acpica/acparser.h +++ b/drivers/acpi/acpica/acparser.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 f600aded7261..a48d713e9599 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,7 +48,7 @@ * * Return Package types * - * 1) PTYPE1 packages do not contain sub-packages. + * 1) PTYPE1 packages do not contain subpackages. * * ACPI_PTYPE1_FIXED: Fixed-length length, 1 or 2 object types: * object type @@ -63,8 +63,8 @@ * (Used for _PRW) * * - * 2) PTYPE2 packages contain a Variable-length number of sub-packages. Each - * of the different types describe the contents of each of the sub-packages. + * 2) PTYPE2 packages contain a Variable-length number of subpackages. Each + * of the different types describe the contents of each of the subpackages. * * ACPI_PTYPE2: Each subpackage contains 1 or 2 object types. Zero-length * parent package is allowed: @@ -560,7 +560,7 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { /* * For _HPX, a single package is returned, containing a variable-length number - * of sub-packages. Each sub-package contains a PCI record setting. + * of subpackages. Each subpackage contains a PCI record setting. * There are several different type of record settings, of different * lengths, but all elements of all settings are Integers. */ @@ -698,6 +698,12 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + {{"_PRP", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: 1 Str, 1 Int/Str/Pkg */ + PACKAGE_INFO(ACPI_PTYPE2, ACPI_RTYPE_STRING, 1, + ACPI_RTYPE_INTEGER | ACPI_RTYPE_STRING | + ACPI_RTYPE_PACKAGE | ACPI_RTYPE_REFERENCE, 1, 0), + {{"_PRS", METHOD_0ARGS, METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h index ff97430455cb..4b008e8884a1 100644 --- a/drivers/acpi/acpica/acresrc.h +++ b/drivers/acpi/acpica/acresrc.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 fc83c0a5ca70..5d2989a1b68c 100644 --- a/drivers/acpi/acpica/acstruct.h +++ b/drivers/acpi/acpica/acstruct.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 c54f42c64fe2..5fa4b2027697 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 be8180c17d7e..ceeec0b7ccb1 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,7 +49,7 @@ extern const u8 acpi_gbl_resource_aml_serial_bus_sizes[]; /* Strings used by the disassembler and debugger resource dump routines */ -#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined (ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER) extern const char *acpi_gbl_bm_decode[]; extern const char *acpi_gbl_config_decode[]; diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index 48a3e331b72d..5908ccec6aea 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -7,7 +7,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 87c26366d1df..f3f834408441 100644 --- a/drivers/acpi/acpica/amlresrc.h +++ b/drivers/acpi/acpica/amlresrc.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 afdc6df17abf..720b1cdda711 100644 --- a/drivers/acpi/acpica/dsargs.c +++ b/drivers/acpi/acpica/dsargs.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 eb56b66444b5..8daf9de82b73 100644 --- a/drivers/acpi/acpica/dscontrol.c +++ b/drivers/acpi/acpica/dscontrol.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 e7a57c554e84..3661c8e90540 100644 --- a/drivers/acpi/acpica/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 14424200d246..96644d5ac0e1 100644 --- a/drivers/acpi/acpica/dsinit.c +++ b/drivers/acpi/acpica/dsinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 81a78ba84311..2c6d42c2bc01 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 c4b0b3657237..b67522df01ac 100644 --- a/drivers/acpi/acpica/dsmthdat.c +++ b/drivers/acpi/acpica/dsmthdat.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 b1746a68dad1..a1e7e6b6fcf7 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 5205edcf2c01..6c0759c0db47 100644 --- a/drivers/acpi/acpica/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 d7f53fb2979a..9f74795e2268 100644 --- a/drivers/acpi/acpica/dsutils.c +++ b/drivers/acpi/acpica/dsutils.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 1bbb22fd6fa0..f7f5107e754d 100644 --- a/drivers/acpi/acpica/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 2dbe109727c8..bd7811c64169 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 7f569d573027..2ac28d297305 100644 --- a/drivers/acpi/acpica/dswload2.c +++ b/drivers/acpi/acpica/dswload2.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 d67891de1b54..9d6e2c1de1f8 100644 --- a/drivers/acpi/acpica/dswscope.c +++ b/drivers/acpi/acpica/dswscope.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 ecb12e2137ff..24f7d5ea678a 100644 --- a/drivers/acpi/acpica/dswstate.c +++ b/drivers/acpi/acpica/dswstate.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 83cd45f4a870..c7bffff9ed32 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 4c67193a9fa7..3393a73ca0d6 100644 --- a/drivers/acpi/acpica/evglock.c +++ b/drivers/acpi/acpica/evglock.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 a9cb4a1a4bb8..955f83da68a5 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index a31e549e64cc..caaed3c673fd 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index a3e2f38aadf6..ae779c1e871d 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c index 4d764e847a08..17e4bbfdb096 100644 --- a/drivers/acpi/acpica/evgpeutil.c +++ b/drivers/acpi/acpica/evgpeutil.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c index e3157313eb27..78ac29351c9e 100644 --- a/drivers/acpi/acpica/evhandler.c +++ b/drivers/acpi/acpica/evhandler.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 a5687540e9a6..5d594eb2e5ec 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 144cbb9b73bc..9957297d1580 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -314,6 +314,7 @@ acpi_ev_detach_region(union acpi_operand_object *region_obj, { union acpi_operand_object *handler_obj; union acpi_operand_object *obj_desc; + union acpi_operand_object *start_desc; union acpi_operand_object **last_obj_ptr; acpi_adr_space_setup region_setup; void **region_context; @@ -341,6 +342,7 @@ acpi_ev_detach_region(union acpi_operand_object *region_obj, /* Find this region in the handler's list */ obj_desc = handler_obj->address_space.region_list; + start_desc = obj_desc; last_obj_ptr = &handler_obj->address_space.region_list; while (obj_desc) { @@ -438,6 +440,15 @@ acpi_ev_detach_region(union acpi_operand_object *region_obj, last_obj_ptr = &obj_desc->region.next; obj_desc = obj_desc->region.next; + + /* Prevent infinite loop if list is corrupted */ + + if (obj_desc == start_desc) { + ACPI_ERROR((AE_INFO, + "Circular handler list in region object %p", + region_obj)); + return_VOID; + } } /* If we get here, the region was not in the handler's region list */ diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index 8354c4f7f10c..1b148a440d67 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 9e9e3454d893..4d8a709c1fc4 100644 --- a/drivers/acpi/acpica/evsci.c +++ b/drivers/acpi/acpica/evsci.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 23a7fadca412..a734b27da061 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index 39d06af5e347..e286640ad4ff 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 5713da77c665..20a1392ffe06 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -583,6 +583,18 @@ acpi_install_gpe_block(acpi_handle gpe_device, goto unlock_and_exit; } + /* Validate the parent device */ + + if (node->type != ACPI_TYPE_DEVICE) { + status = AE_TYPE; + goto unlock_and_exit; + } + + if (node->object) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + /* * For user-installed GPE Block Devices, the gpe_block_base_number * is always zero @@ -666,6 +678,13 @@ acpi_status acpi_remove_gpe_block(acpi_handle gpe_device) goto unlock_and_exit; } + /* Validate the parent device */ + + if (node->type != ACPI_TYPE_DEVICE) { + status = AE_TYPE; + goto unlock_and_exit; + } + /* Get the device_object attached to the node */ obj_desc = acpi_ns_get_attached_object(node); diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c index 02ed75ac56cd..2d6f187939c7 100644 --- a/drivers/acpi/acpica/evxfregn.c +++ b/drivers/acpi/acpica/evxfregn.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 06d216c8d43a..8ba1464efd11 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 69e4a8cc9b71..c545386fee96 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 3c2e6dcdad3e..95d23dabcfbb 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 81c72a4ecd82..4cfc3d3b5c97 100644 --- a/drivers/acpi/acpica/exdebug.c +++ b/drivers/acpi/acpica/exdebug.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 4d046faac48c..973fdae00f94 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -94,12 +94,13 @@ static struct acpi_exdump_info acpi_ex_dump_buffer[5] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_buffer), NULL}, {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(buffer.length), "Length"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(buffer.pointer), "Pointer"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(buffer.node), "Parent Node"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(buffer.node), "Parent Node"}, {ACPI_EXD_BUFFER, 0, NULL} }; -static struct acpi_exdump_info acpi_ex_dump_package[5] = { +static struct acpi_exdump_info acpi_ex_dump_package[6] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_package), NULL}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(package.node), "Parent Node"}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(package.flags), "Flags"}, {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(package.count), "Elements"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(package.elements), "Element List"}, @@ -108,11 +109,11 @@ static struct acpi_exdump_info acpi_ex_dump_package[5] = { static struct acpi_exdump_info acpi_ex_dump_device[4] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_device), NULL}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.handler), "Handler"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.notify_list[0]), "System Notify"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(device.notify_list[1]), - "Device Notify"} + "Device Notify"}, + {ACPI_EXD_HDLR_LIST, ACPI_EXD_OFFSET(device.handler), "Handler"} }; static struct acpi_exdump_info acpi_ex_dump_event[2] = { @@ -142,17 +143,18 @@ static struct acpi_exdump_info acpi_ex_dump_mutex[5] = { {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.os_mutex), "OsMutex"} }; -static struct acpi_exdump_info acpi_ex_dump_region[7] = { +static struct acpi_exdump_info acpi_ex_dump_region[8] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_region), NULL}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(region.space_id), "Space Id"}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(region.flags), "Flags"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(region.node), "Parent Node"}, {ACPI_EXD_ADDRESS, ACPI_EXD_OFFSET(region.address), "Address"}, {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(region.length), "Length"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(region.handler), "Handler"}, + {ACPI_EXD_HDLR_LIST, ACPI_EXD_OFFSET(region.handler), "Handler"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(region.next), "Next"} }; -static struct acpi_exdump_info acpi_ex_dump_power[5] = { +static struct acpi_exdump_info acpi_ex_dump_power[6] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_power), NULL}, {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(power_resource.system_level), "System Level"}, @@ -161,7 +163,8 @@ static struct acpi_exdump_info acpi_ex_dump_power[5] = { {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.notify_list[0]), "System Notify"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.notify_list[1]), - "Device Notify"} + "Device Notify"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(power_resource.handler), "Handler"} }; static struct acpi_exdump_info acpi_ex_dump_processor[7] = { @@ -225,7 +228,7 @@ static struct acpi_exdump_info acpi_ex_dump_reference[8] = { {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(reference.target_type), "Target Type"}, {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(reference.value), "Value"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(reference.object), "Object Desc"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(reference.node), "Node"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(reference.node), "Node"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(reference.where), "Where"}, {ACPI_EXD_REFERENCE, 0, NULL} }; @@ -234,16 +237,16 @@ static struct acpi_exdump_info acpi_ex_dump_address_handler[6] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_address_handler), NULL}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(address_space.space_id), "Space Id"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(address_space.next), "Next"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(address_space.region_list), + {ACPI_EXD_HDLR_LIST, ACPI_EXD_OFFSET(address_space.next), "Next"}, + {ACPI_EXD_RGN_LIST, ACPI_EXD_OFFSET(address_space.region_list), "Region List"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(address_space.node), "Node"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(address_space.node), "Node"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(address_space.context), "Context"} }; static struct acpi_exdump_info acpi_ex_dump_notify[7] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_notify), NULL}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.node), "Node"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(notify.node), "Node"}, {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(notify.handler_type), "Handler Type"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.handler), "Handler"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.context), "Context"}, @@ -252,14 +255,31 @@ static struct acpi_exdump_info acpi_ex_dump_notify[7] = { {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(notify.next[1]), "Next Device Notify"} }; +static struct acpi_exdump_info acpi_ex_dump_extra[6] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_extra), NULL}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(extra.method_REG), "_REG Method"}, + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(extra.scope_node), "Scope Node"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(extra.region_context), + "Region Context"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(extra.aml_start), "Aml Start"}, + {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(extra.aml_length), "Aml Length"} +}; + +static struct acpi_exdump_info acpi_ex_dump_data[3] = { + {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_data), NULL}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(data.handler), "Handler"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(data.pointer), "Raw Data"} +}; + /* Miscellaneous tables */ -static struct acpi_exdump_info acpi_ex_dump_common[4] = { +static struct acpi_exdump_info acpi_ex_dump_common[5] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_common), NULL}, {ACPI_EXD_TYPE, 0, NULL}, {ACPI_EXD_UINT16, ACPI_EXD_OFFSET(common.reference_count), "Reference Count"}, - {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(common.flags), "Flags"} + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(common.flags), "Flags"}, + {ACPI_EXD_LIST, ACPI_EXD_OFFSET(common.next_object), "Object List"} }; static struct acpi_exdump_info acpi_ex_dump_field_common[7] = { @@ -274,15 +294,17 @@ static struct acpi_exdump_info acpi_ex_dump_field_common[7] = { "Field Bit Offset"}, {ACPI_EXD_UINT32, ACPI_EXD_OFFSET(common_field.base_byte_offset), "Base Byte Offset"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(common_field.node), "Parent Node"} + {ACPI_EXD_NODE, ACPI_EXD_OFFSET(common_field.node), "Parent Node"} }; -static struct acpi_exdump_info acpi_ex_dump_node[5] = { +static struct acpi_exdump_info acpi_ex_dump_node[7] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_node), NULL}, {ACPI_EXD_UINT8, ACPI_EXD_NSOFFSET(flags), "Flags"}, {ACPI_EXD_UINT8, ACPI_EXD_NSOFFSET(owner_id), "Owner Id"}, - {ACPI_EXD_POINTER, ACPI_EXD_NSOFFSET(child), "Child List"}, - {ACPI_EXD_POINTER, ACPI_EXD_NSOFFSET(peer), "Next Peer"} + {ACPI_EXD_LIST, ACPI_EXD_NSOFFSET(object), "Object List"}, + {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET(parent), "Parent"}, + {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET(child), "Child"}, + {ACPI_EXD_NODE, ACPI_EXD_NSOFFSET(peer), "Peer"} }; /* Dispatch table, indexed by object type */ @@ -315,7 +337,9 @@ static struct acpi_exdump_info *acpi_ex_dump_info[] = { acpi_ex_dump_address_handler, NULL, NULL, - NULL + NULL, + acpi_ex_dump_extra, + acpi_ex_dump_data }; /******************************************************************************* @@ -340,6 +364,10 @@ acpi_ex_dump_object(union acpi_operand_object *obj_desc, char *name; const char *reference_name; u8 count; + union acpi_operand_object *start; + union acpi_operand_object *data = NULL; + union acpi_operand_object *next; + struct acpi_namespace_node *node; if (!info) { acpi_os_printf @@ -363,9 +391,9 @@ acpi_ex_dump_object(union acpi_operand_object *obj_desc, case ACPI_EXD_TYPE: - acpi_ex_out_string("Type", - acpi_ut_get_object_type_name - (obj_desc)); + acpi_os_printf("%20s : %2.2X [%s]\n", "Type", + obj_desc->common.type, + acpi_ut_get_object_type_name(obj_desc)); break; case ACPI_EXD_UINT8: @@ -433,6 +461,121 @@ acpi_ex_dump_object(union acpi_operand_object *obj_desc, acpi_ex_dump_reference_obj(obj_desc); break; + case ACPI_EXD_LIST: + + start = *ACPI_CAST_PTR(void *, target); + next = start; + + acpi_os_printf("%20s : %p", name, next); + if (next) { + acpi_os_printf("(%s %2.2X)", + acpi_ut_get_object_type_name + (next), next->common.type); + + while (next->common.next_object) { + if ((next->common.type == + ACPI_TYPE_LOCAL_DATA) && !data) { + data = next; + } + + next = next->common.next_object; + acpi_os_printf("->%p(%s %2.2X)", next, + acpi_ut_get_object_type_name + (next), + next->common.type); + + if ((next == start) || (next == data)) { + acpi_os_printf + ("\n**** Error: Object list appears to be circular linked"); + break; + } + } + } + + acpi_os_printf("\n", next); + break; + + case ACPI_EXD_HDLR_LIST: + + start = *ACPI_CAST_PTR(void *, target); + next = start; + + acpi_os_printf("%20s : %p", name, next); + if (next) { + acpi_os_printf("(%s %2.2X)", + acpi_ut_get_object_type_name + (next), next->common.type); + + while (next->address_space.next) { + if ((next->common.type == + ACPI_TYPE_LOCAL_DATA) && !data) { + data = next; + } + + next = next->address_space.next; + acpi_os_printf("->%p(%s %2.2X)", next, + acpi_ut_get_object_type_name + (next), + next->common.type); + + if ((next == start) || (next == data)) { + acpi_os_printf + ("\n**** Error: Handler list appears to be circular linked"); + break; + } + } + } + + acpi_os_printf("\n", next); + break; + + case ACPI_EXD_RGN_LIST: + + start = *ACPI_CAST_PTR(void *, target); + next = start; + + acpi_os_printf("%20s : %p", name, next); + if (next) { + acpi_os_printf("(%s %2.2X)", + acpi_ut_get_object_type_name + (next), next->common.type); + + while (next->region.next) { + if ((next->common.type == + ACPI_TYPE_LOCAL_DATA) && !data) { + data = next; + } + + next = next->region.next; + acpi_os_printf("->%p(%s %2.2X)", next, + acpi_ut_get_object_type_name + (next), + next->common.type); + + if ((next == start) || (next == data)) { + acpi_os_printf + ("\n**** Error: Region list appears to be circular linked"); + break; + } + } + } + + acpi_os_printf("\n", next); + break; + + case ACPI_EXD_NODE: + + node = + *ACPI_CAST_PTR(struct acpi_namespace_node *, + target); + + acpi_os_printf("%20s : %p", name, node); + if (node) { + acpi_os_printf(" [%4.4s]", node->name.ascii); + } + acpi_os_printf("\n"); + break; + default: acpi_os_printf("**** Invalid table opcode [%X] ****\n", @@ -821,10 +964,8 @@ void acpi_ex_dump_namespace_node(struct acpi_namespace_node *node, u32 flags) } acpi_os_printf("%20s : %4.4s\n", "Name", acpi_ut_get_node_name(node)); - acpi_ex_out_string("Type", acpi_ut_get_type_name(node->type)); - acpi_ex_out_pointer("Attached Object", - acpi_ns_get_attached_object(node)); - acpi_ex_out_pointer("Parent", node->parent); + acpi_os_printf("%20s : %2.2X [%s]\n", "Type", + node->type, acpi_ut_get_type_name(node->type)); acpi_ex_dump_object(ACPI_CAST_PTR(union acpi_operand_object, node), acpi_ex_dump_node); @@ -1017,22 +1158,26 @@ acpi_ex_dump_object_descriptor(union acpi_operand_object *obj_desc, u32 flags) ((struct acpi_namespace_node *)obj_desc)-> object); - acpi_ex_dump_object_descriptor(((struct acpi_namespace_node *) - obj_desc)->object, flags); - return_VOID; + obj_desc = ((struct acpi_namespace_node *)obj_desc)->object; + goto dump_object; } if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) != ACPI_DESC_TYPE_OPERAND) { - acpi_os_printf - ("ExDumpObjectDescriptor: %p is not an ACPI operand object: [%s]\n", - obj_desc, acpi_ut_get_descriptor_name(obj_desc)); + acpi_os_printf("%p is not an ACPI operand object: [%s]\n", + obj_desc, acpi_ut_get_descriptor_name(obj_desc)); return_VOID; } - if (obj_desc->common.type > ACPI_TYPE_NS_NODE_MAX) { + /* Validate the object type */ + + if (obj_desc->common.type > ACPI_TYPE_LOCAL_MAX) { + acpi_os_printf("Not a known object type: %2.2X\n", + obj_desc->common.type); return_VOID; } +dump_object: + /* Common Fields */ acpi_ex_dump_object(obj_desc, acpi_ex_dump_common); @@ -1040,6 +1185,22 @@ acpi_ex_dump_object_descriptor(union acpi_operand_object *obj_desc, u32 flags) /* Object-specific fields */ acpi_ex_dump_object(obj_desc, acpi_ex_dump_info[obj_desc->common.type]); + + if (obj_desc->common.type == ACPI_TYPE_REGION) { + obj_desc = obj_desc->common.next_object; + if (obj_desc->common.type > ACPI_TYPE_LOCAL_MAX) { + acpi_os_printf + ("Secondary object is not a known object type: %2.2X\n", + obj_desc->common.type); + + return_VOID; + } + + acpi_os_printf("\nExtra attached Object (%p):\n", obj_desc); + acpi_ex_dump_object(obj_desc, + acpi_ex_dump_info[obj_desc->common.type]); + } + return_VOID; } diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c index cfd875243421..68d97441432c 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 49fb742d61b9..1d1b27a96c5b 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 65d93607f368..2207e624f538 100644 --- a/drivers/acpi/acpica/exmisc.c +++ b/drivers/acpi/acpica/exmisc.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 7be0205ad067..b49ea2a95f4f 100644 --- a/drivers/acpi/acpica/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 14689dec4960..dbb03b544e8c 100644 --- a/drivers/acpi/acpica/exnames.c +++ b/drivers/acpi/acpica/exnames.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 d74cea416ca0..1b8e94104407 100644 --- a/drivers/acpi/acpica/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 d6fa0fce1fc9..2ede656ee26a 100644 --- a/drivers/acpi/acpica/exoparg2.c +++ b/drivers/acpi/acpica/exoparg2.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 bc042adf8804..363767cf01e5 100644 --- a/drivers/acpi/acpica/exoparg3.c +++ b/drivers/acpi/acpica/exoparg3.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 4459e32c683d..29e9e99f7fe3 100644 --- a/drivers/acpi/acpica/exoparg6.c +++ b/drivers/acpi/acpica/exoparg6.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 5a588611ab48..ee3f872870bc 100644 --- a/drivers/acpi/acpica/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 9d28867e60dc..cd5288a257a9 100644 --- a/drivers/acpi/acpica/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 7ca6925a87ca..ab060261b43e 100644 --- a/drivers/acpi/acpica/exresnte.c +++ b/drivers/acpi/acpica/exresnte.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 1606524312e3..3cde553bcbe1 100644 --- a/drivers/acpi/acpica/exresolv.c +++ b/drivers/acpi/acpica/exresolv.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 be3f66973ee8..3af8de3fcea4 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 f0b09bf9887d..daf49f7ea311 100644 --- a/drivers/acpi/acpica/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 20d809d90c5b..04bd16c08f9e 100644 --- a/drivers/acpi/acpica/exstoren.c +++ b/drivers/acpi/acpica/exstoren.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 26e371073b1a..fd11018b0168 100644 --- a/drivers/acpi/acpica/exstorob.c +++ b/drivers/acpi/acpica/exstorob.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 6578dee2e51b..841caed11c08 100644 --- a/drivers/acpi/acpica/exsystem.c +++ b/drivers/acpi/acpica/exsystem.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 99dc7b287d55..5b16c5484bee 100644 --- a/drivers/acpi/acpica/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 3d36df828f52..1e66d960fc11 100644 --- a/drivers/acpi/acpica/hwacpi.c +++ b/drivers/acpi/acpica/hwacpi.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 414076818d40..858fdd6be598 100644 --- a/drivers/acpi/acpica/hwesleep.c +++ b/drivers/acpi/acpica/hwesleep.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 96540506058f..2e6caabba07a 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwpci.c b/drivers/acpi/acpica/hwpci.c index 0889a629505f..e701d8c33dbf 100644 --- a/drivers/acpi/acpica/hwpci.c +++ b/drivers/acpi/acpica/hwpci.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 12e6cff54f78..e0fd9b4978cd 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 e3828cc4361b..d590693eb54e 100644 --- a/drivers/acpi/acpica/hwsleep.c +++ b/drivers/acpi/acpica/hwsleep.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 3c498dc1636e..76ab5c1a814e 100644 --- a/drivers/acpi/acpica/hwtimer.c +++ b/drivers/acpi/acpica/hwtimer.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 eab70d58852a..6b919127cd9d 100644 --- a/drivers/acpi/acpica/hwvalid.c +++ b/drivers/acpi/acpica/hwvalid.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 b4b47db2dee2..96d007df65ec 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 15dddc10fc9b..6921c7f3d208 100644 --- a/drivers/acpi/acpica/hwxfsleep.c +++ b/drivers/acpi/acpica/hwxfsleep.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 14f65f6345b9..f1249e3463be 100644 --- a/drivers/acpi/acpica/nsaccess.c +++ b/drivers/acpi/acpica/nsaccess.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 fd1ff54cda19..607eb9e5150d 100644 --- a/drivers/acpi/acpica/nsalloc.c +++ b/drivers/acpi/acpica/nsalloc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 74b24c82707e..80fcfc8c9c1b 100644 --- a/drivers/acpi/acpica/nsarguments.c +++ b/drivers/acpi/acpica/nsarguments.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 acd2964c2690..b55642c4ee58 100644 --- a/drivers/acpi/acpica/nsconvert.c +++ b/drivers/acpi/acpica/nsconvert.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 48b9c6f12643..3d88ef4a3e0d 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 283762511b73..42d37109aa5d 100644 --- a/drivers/acpi/acpica/nsdumpdv.c +++ b/drivers/acpi/acpica/nsdumpdv.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 963ceef063f8..e634a05974db 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 3a0423af968c..5b74677bf74d 100644 --- a/drivers/acpi/acpica/nsinit.c +++ b/drivers/acpi/acpica/nsinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 89ec645e7730..7ae521ce8d3f 100644 --- a/drivers/acpi/acpica/nsload.c +++ b/drivers/acpi/acpica/nsload.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 90a0380fb8a0..7eee0a6f02f6 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 7a736f4d1fd8..fe54a8c73b8c 100644 --- a/drivers/acpi/acpica/nsobject.c +++ b/drivers/acpi/acpica/nsobject.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -222,13 +222,19 @@ void acpi_ns_detach_object(struct acpi_namespace_node *node) } } - /* Clear the entry in all cases */ + /* Clear the Node entry in all cases */ node->object = NULL; if (ACPI_GET_DESCRIPTOR_TYPE(obj_desc) == ACPI_DESC_TYPE_OPERAND) { + + /* Unlink object from front of possible object list */ + node->object = obj_desc->common.next_object; + + /* Handle possible 2-descriptor object */ + if (node->object && - ((node->object)->common.type != ACPI_TYPE_LOCAL_DATA)) { + (node->object->common.type != ACPI_TYPE_LOCAL_DATA)) { node->object = node->object->common.next_object; } } diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c index 177857340271..e83cff31754b 100644 --- a/drivers/acpi/acpica/nsparse.c +++ b/drivers/acpi/acpica/nsparse.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 d2855d9857c4..392910ffbed9 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 3d5391f9bcb5..68f725839eb6 100644 --- a/drivers/acpi/acpica/nsprepkg.c +++ b/drivers/acpi/acpica/nsprepkg.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -132,12 +132,12 @@ acpi_ns_check_package(struct acpi_evaluate_info *info, * Decode the type of the expected package contents * * PTYPE1 packages contain no subpackages - * PTYPE2 packages contain sub-packages + * PTYPE2 packages contain subpackages */ switch (package->ret_info.type) { case ACPI_PTYPE1_FIXED: /* - * The package count is fixed and there are no sub-packages + * The package count is fixed and there are no subpackages * * If package is too small, exit. * If package is larger than expected, issue warning but continue @@ -169,7 +169,7 @@ acpi_ns_check_package(struct acpi_evaluate_info *info, case ACPI_PTYPE1_VAR: /* - * The package count is variable, there are no sub-packages, and all + * The package count is variable, there are no subpackages, and all * elements must be of the same type */ for (i = 0; i < count; i++) { @@ -185,7 +185,7 @@ acpi_ns_check_package(struct acpi_evaluate_info *info, case ACPI_PTYPE1_OPTION: /* - * The package count is variable, there are no sub-packages. There are + * The package count is variable, there are no subpackages. There are * a fixed number of required elements, and a variable number of * optional elements. * @@ -242,7 +242,7 @@ acpi_ns_check_package(struct acpi_evaluate_info *info, elements++; count--; - /* Examine the sub-packages */ + /* Examine the subpackages */ status = acpi_ns_check_package_list(info, package, elements, count); @@ -250,7 +250,7 @@ acpi_ns_check_package(struct acpi_evaluate_info *info, case ACPI_PTYPE2_PKG_COUNT: - /* First element is the (Integer) count of sub-packages to follow */ + /* First element is the (Integer) count of subpackages to follow */ status = acpi_ns_check_object_type(info, elements, ACPI_RTYPE_INTEGER, 0); @@ -270,7 +270,7 @@ acpi_ns_check_package(struct acpi_evaluate_info *info, count = expected_count; elements++; - /* Examine the sub-packages */ + /* Examine the subpackages */ status = acpi_ns_check_package_list(info, package, elements, count); @@ -283,9 +283,9 @@ acpi_ns_check_package(struct acpi_evaluate_info *info, case ACPI_PTYPE2_FIX_VAR: /* * These types all return a single Package that consists of a - * variable number of sub-Packages. + * variable number of subpackages. * - * First, ensure that the first element is a sub-Package. If not, + * First, ensure that the first element is a subpackage. If not, * the BIOS may have incorrectly returned the object as a single * package instead of a Package of Packages (a common error if * there is only one entry). We may be able to repair this by @@ -310,7 +310,7 @@ acpi_ns_check_package(struct acpi_evaluate_info *info, count = 1; } - /* Examine the sub-packages */ + /* Examine the subpackages */ status = acpi_ns_check_package_list(info, package, elements, count); @@ -370,9 +370,9 @@ acpi_ns_check_package_list(struct acpi_evaluate_info *info, u32 j; /* - * Validate each sub-Package in the parent Package + * Validate each subpackage in the parent Package * - * NOTE: assumes list of sub-packages contains no NULL elements. + * NOTE: assumes list of subpackages contains no NULL elements. * Any NULL elements should have been removed by earlier call * to acpi_ns_remove_null_elements. */ @@ -389,7 +389,7 @@ acpi_ns_check_package_list(struct acpi_evaluate_info *info, return (status); } - /* Examine the different types of expected sub-packages */ + /* Examine the different types of expected subpackages */ info->parent_package = sub_package; switch (package->ret_info.type) { @@ -450,14 +450,14 @@ acpi_ns_check_package_list(struct acpi_evaluate_info *info, case ACPI_PTYPE2_FIXED: - /* Each sub-package has a fixed length */ + /* Each subpackage has a fixed length */ expected_count = package->ret_info2.count; if (sub_package->package.count < expected_count) { goto package_too_small; } - /* Check the type of each sub-package element */ + /* Check the type of each subpackage element */ for (j = 0; j < expected_count; j++) { status = @@ -475,14 +475,14 @@ acpi_ns_check_package_list(struct acpi_evaluate_info *info, case ACPI_PTYPE2_MIN: - /* Each sub-package has a variable but minimum length */ + /* Each subpackage has a variable but minimum length */ expected_count = package->ret_info.count1; if (sub_package->package.count < expected_count) { goto package_too_small; } - /* Check the type of each sub-package element */ + /* Check the type of each subpackage element */ status = acpi_ns_check_package_elements(info, sub_elements, @@ -531,7 +531,7 @@ acpi_ns_check_package_list(struct acpi_evaluate_info *info, (*sub_elements)->integer.value = expected_count; } - /* Check the type of each sub-package element */ + /* Check the type of each subpackage element */ status = acpi_ns_check_package_elements(info, @@ -557,10 +557,10 @@ acpi_ns_check_package_list(struct acpi_evaluate_info *info, package_too_small: - /* The sub-package count was smaller than required */ + /* The subpackage count was smaller than required */ ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags, - "Return Sub-Package[%u] is too small - found %u elements, expected %u", + "Return SubPackage[%u] is too small - found %u elements, expected %u", i, sub_package->package.count, expected_count)); return (AE_AML_OPERAND_VALUE); diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index a05afff50eb9..7e417aa5c91e 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -207,13 +207,30 @@ acpi_ns_simple_repair(struct acpi_evaluate_info *info, * this predefined name. Either one return value is expected, or none, * for both methods and other objects. * - * Exit now if there is no return object. Warning if one was expected. + * Try to fix if there was no return object. Warning if failed to fix. */ if (!return_object) { if (expected_btypes && (!(expected_btypes & ACPI_RTYPE_NONE))) { - ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, - ACPI_WARN_ALWAYS, - "Missing expected return value")); + if (package_index != ACPI_NOT_PACKAGE_ELEMENT) { + ACPI_WARN_PREDEFINED((AE_INFO, + info->full_pathname, + ACPI_WARN_ALWAYS, + "Found unexpected NULL package element")); + + status = + acpi_ns_repair_null_element(info, + expected_btypes, + package_index, + return_object_ptr); + if (ACPI_SUCCESS(status)) { + return (AE_OK); /* Repair was successful */ + } + } else { + ACPI_WARN_PREDEFINED((AE_INFO, + info->full_pathname, + ACPI_WARN_ALWAYS, + "Missing expected return value")); + } return (AE_AML_NO_RETURN_VALUE); } @@ -448,7 +465,7 @@ acpi_ns_repair_null_element(struct acpi_evaluate_info * info, * RETURN: None. * * DESCRIPTION: Remove all NULL package elements from packages that contain - * a variable number of sub-packages. For these types of + * a variable number of subpackages. For these types of * packages, NULL elements can be safely removed. * *****************************************************************************/ @@ -469,7 +486,7 @@ acpi_ns_remove_null_elements(struct acpi_evaluate_info *info, /* * We can safely remove all NULL elements from these package types: * PTYPE1_VAR packages contain a variable number of simple data types. - * PTYPE2 packages contain a variable number of sub-packages. + * PTYPE2 packages contain a variable number of subpackages. */ switch (package_type) { case ACPI_PTYPE1_VAR: diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index 6a25d320b169..b09e6bef72b8 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -432,8 +432,8 @@ acpi_ns_repair_CID(struct acpi_evaluate_info *info, * DESCRIPTION: Repair for the _CST object: * 1. Sort the list ascending by C state type * 2. Ensure type cannot be zero - * 3. A sub-package count of zero means _CST is meaningless - * 4. Count must match the number of C state sub-packages + * 3. A subpackage count of zero means _CST is meaningless + * 4. Count must match the number of C state subpackages * *****************************************************************************/ @@ -611,6 +611,7 @@ acpi_ns_repair_PRT(struct acpi_evaluate_info *info, union acpi_operand_object **top_object_list; union acpi_operand_object **sub_object_list; union acpi_operand_object *obj_desc; + union acpi_operand_object *sub_package; u32 element_count; u32 index; @@ -619,8 +620,17 @@ acpi_ns_repair_PRT(struct acpi_evaluate_info *info, top_object_list = package_object->package.elements; element_count = package_object->package.count; - for (index = 0; index < element_count; index++) { - sub_object_list = (*top_object_list)->package.elements; + /* Examine each subpackage */ + + for (index = 0; index < element_count; index++, top_object_list++) { + sub_package = *top_object_list; + sub_object_list = sub_package->package.elements; + + /* Check for minimum required element count */ + + if (sub_package->package.count < 4) { + continue; + } /* * If the BIOS has erroneously reversed the _PRT source_name (index 2) @@ -634,15 +644,12 @@ acpi_ns_repair_PRT(struct acpi_evaluate_info *info, sub_object_list[2] = obj_desc; info->return_flags |= ACPI_OBJECT_REPAIRED; - ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + ACPI_WARN_PREDEFINED((AE_INFO, + info->full_pathname, info->node_flags, "PRT[%X]: Fixed reversed SourceName and SourceIndex", index)); } - - /* Point to the next union acpi_operand_object in the top level package */ - - top_object_list++; } return (AE_OK); @@ -679,7 +686,7 @@ acpi_ns_repair_PSS(struct acpi_evaluate_info *info, u32 i; /* - * Entries (sub-packages) in the _PSS Package must be sorted by power + * Entries (subpackages) in the _PSS Package must be sorted by power * dissipation, in descending order. If it appears that the list is * incorrectly sorted, sort it. We sort by cpu_frequency, since this * should be proportional to the power. @@ -767,9 +774,9 @@ acpi_ns_repair_TSS(struct acpi_evaluate_info *info, * * PARAMETERS: info - Method execution information block * return_object - Pointer to the top-level returned object - * start_index - Index of the first sub-package - * expected_count - Minimum length of each sub-package - * sort_index - Sub-package entry to sort on + * start_index - Index of the first subpackage + * expected_count - Minimum length of each subpackage + * sort_index - Subpackage entry to sort on * sort_direction - Ascending or descending * sort_key_name - Name of the sort_index field * @@ -805,7 +812,7 @@ acpi_ns_check_sorted_list(struct acpi_evaluate_info *info, } /* - * NOTE: assumes list of sub-packages contains no NULL elements. + * NOTE: assumes list of subpackages contains no NULL elements. * Any NULL elements should have been removed by earlier call * to acpi_ns_remove_null_elements. */ @@ -832,7 +839,7 @@ acpi_ns_check_sorted_list(struct acpi_evaluate_info *info, return (AE_AML_OPERAND_TYPE); } - /* Each sub-package must have the minimum length */ + /* Each subpackage must have the minimum length */ if ((*outer_elements)->package.count < expected_count) { return (AE_AML_PACKAGE_LIMIT); diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c index 47420faef073..af1cc42a8aa1 100644 --- a/drivers/acpi/acpica/nssearch.c +++ b/drivers/acpi/acpica/nssearch.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 4a0665b6bcc1..4a5e3f5c0ff7 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 e81f15ef659a..4758a1f2ce22 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 1f0c28ba50df..4bd558bf10d2 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -923,19 +923,22 @@ ACPI_EXPORT_SYMBOL(acpi_detach_data) /******************************************************************************* * - * FUNCTION: acpi_get_data + * FUNCTION: acpi_get_data_full * * PARAMETERS: obj_handle - Namespace node * handler - Handler used in call to attach_data * data - Where the data is returned + * callback - function to execute before returning * * RETURN: Status * - * DESCRIPTION: Retrieve data that was previously attached to a namespace node. + * DESCRIPTION: Retrieve data that was previously attached to a namespace node + * and execute a callback before returning. * ******************************************************************************/ acpi_status -acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data) +acpi_get_data_full(acpi_handle obj_handle, acpi_object_handler handler, + void **data, void (*callback)(void *)) { struct acpi_namespace_node *node; acpi_status status; @@ -960,10 +963,34 @@ acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data) } status = acpi_ns_get_attached_data(node, handler, data); + if (ACPI_SUCCESS(status) && callback) { + callback(*data); + } unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return (status); } +ACPI_EXPORT_SYMBOL(acpi_get_data_full) + +/******************************************************************************* + * + * FUNCTION: acpi_get_data + * + * PARAMETERS: obj_handle - Namespace node + * handler - Handler used in call to attach_data + * data - Where the data is returned + * + * RETURN: Status + * + * DESCRIPTION: Retrieve data that was previously attached to a namespace node. + * + ******************************************************************************/ +acpi_status +acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data) +{ + return acpi_get_data_full(obj_handle, handler, data, NULL); +} + ACPI_EXPORT_SYMBOL(acpi_get_data) diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index 3a4bd3ff49a3..8c6c11ce9760 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 0e6d79e462d4..dae9401be7a2 100644 --- a/drivers/acpi/acpica/nsxfobj.c +++ b/drivers/acpi/acpica/nsxfobj.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c index 91a5a69db80c..314d314340ae 100644 --- a/drivers/acpi/acpica/psargs.c +++ b/drivers/acpi/acpica/psargs.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 065b44ae538f..646d1a3f6e27 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 95dc608a66a8..af1f46cd37a5 100644 --- a/drivers/acpi/acpica/psobject.c +++ b/drivers/acpi/acpica/psobject.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 1b659e59710a..1755d2ac5656 100644 --- a/drivers/acpi/acpica/psopcode.c +++ b/drivers/acpi/acpica/psopcode.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 b0c9787dbe61..0d8d37ffd04d 100644 --- a/drivers/acpi/acpica/psopinfo.c +++ b/drivers/acpi/acpica/psopinfo.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 79d9a28dedef..6d27b597394e 100644 --- a/drivers/acpi/acpica/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 6a4b6fb39f32..32d250feea21 100644 --- a/drivers/acpi/acpica/psscope.c +++ b/drivers/acpi/acpica/psscope.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 877dc0de8df3..0b64181e7720 100644 --- a/drivers/acpi/acpica/pstree.c +++ b/drivers/acpi/acpica/pstree.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 91fa73a6e55e..3cd48802eede 100644 --- a/drivers/acpi/acpica/psutils.c +++ b/drivers/acpi/acpica/psutils.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 abd65624754f..9cb07e1e76d9 100644 --- a/drivers/acpi/acpica/pswalk.c +++ b/drivers/acpi/acpica/pswalk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 fcb7a840e996..e135acaa5e1c 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 f3a9276ac665..916fd095ff34 100644 --- a/drivers/acpi/acpica/rsaddr.c +++ b/drivers/acpi/acpica/rsaddr.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c index b60c9cf82862..689556744b03 100644 --- a/drivers/acpi/acpica/rscalc.c +++ b/drivers/acpi/acpica/rscalc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -636,7 +636,7 @@ acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object, for (index = 0; index < number_of_elements; index++) { - /* Dereference the sub-package */ + /* Dereference the subpackage */ package_element = *top_object_list; diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c index 3a2ace93e62c..75d369050657 100644 --- a/drivers/acpi/acpica/rscreate.c +++ b/drivers/acpi/acpica/rscreate.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -273,7 +273,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, */ user_prt->length = (sizeof(struct acpi_pci_routing_table) - 4); - /* Each sub-package must be of length 4 */ + /* Each subpackage must be of length 4 */ if ((*top_object_list)->package.count != 4) { ACPI_ERROR((AE_INFO, @@ -283,7 +283,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, } /* - * Dereference the sub-package. + * Dereference the subpackage. * The sub_object_list will now point to an array of the four IRQ * elements: [Address, Pin, Source, source_index] */ @@ -292,7 +292,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, /* 1) First subobject: Dereference the PRT.Address */ obj_desc = sub_object_list[0]; - if (obj_desc->common.type != ACPI_TYPE_INTEGER) { + if (!obj_desc || obj_desc->common.type != ACPI_TYPE_INTEGER) { ACPI_ERROR((AE_INFO, "(PRT[%u].Address) Need Integer, found %s", index, @@ -305,7 +305,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, /* 2) Second subobject: Dereference the PRT.Pin */ obj_desc = sub_object_list[1]; - if (obj_desc->common.type != ACPI_TYPE_INTEGER) { + if (!obj_desc || obj_desc->common.type != ACPI_TYPE_INTEGER) { ACPI_ERROR((AE_INFO, "(PRT[%u].Pin) Need Integer, found %s", index, @@ -394,7 +394,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, /* 4) Fourth subobject: Dereference the PRT.source_index */ obj_desc = sub_object_list[3]; - if (obj_desc->common.type != ACPI_TYPE_INTEGER) { + if (!obj_desc || obj_desc->common.type != ACPI_TYPE_INTEGER) { ACPI_ERROR((AE_INFO, "(PRT[%u].SourceIndex) Need Integer, found %s", index, diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c index 8a2d4986b0aa..c3c56b5a9788 100644 --- a/drivers/acpi/acpica/rsdump.c +++ b/drivers/acpi/acpica/rsdump.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,7 +47,8 @@ #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsdump") -#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUGGER) /* Local prototypes */ static void acpi_rs_out_string(char *title, char *value); diff --git a/drivers/acpi/acpica/rsdumpinfo.c b/drivers/acpi/acpica/rsdumpinfo.c index 46192bd53653..2f9332d5c973 100644 --- a/drivers/acpi/acpica/rsdumpinfo.c +++ b/drivers/acpi/acpica/rsdumpinfo.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,7 +48,7 @@ #define _COMPONENT ACPI_RESOURCES ACPI_MODULE_NAME("rsdumpinfo") -#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUGGER) #define ACPI_RSD_OFFSET(f) (u8) ACPI_OFFSET (union acpi_resource_data,f) #define ACPI_PRT_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_pci_routing_table,f) #define ACPI_RSD_TABLE_SIZE(name) (sizeof(name) / sizeof (struct acpi_rsdump_info)) diff --git a/drivers/acpi/acpica/rsinfo.c b/drivers/acpi/acpica/rsinfo.c index 41fed78e0de6..9d3f8a9a24bd 100644 --- a/drivers/acpi/acpica/rsinfo.c +++ b/drivers/acpi/acpica/rsinfo.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -132,8 +132,7 @@ struct acpi_rsconvert_info *acpi_gbl_convert_resource_serial_bus_dispatch[] = { acpi_rs_convert_uart_serial_bus, }; -#ifdef ACPI_FUTURE_USAGE -#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUGGER) /* Dispatch table for resource dump functions */ @@ -168,7 +167,6 @@ struct acpi_rsdump_info *acpi_gbl_dump_serial_bus_dispatch[] = { }; #endif -#endif /* ACPI_FUTURE_USAGE */ /* * Base sizes for external AML resource descriptors, indexed by internal type. * Includes size of the descriptor header (1 byte for small descriptors, diff --git a/drivers/acpi/acpica/rsio.c b/drivers/acpi/acpica/rsio.c index ca183755a6f9..19d64873290a 100644 --- a/drivers/acpi/acpica/rsio.c +++ b/drivers/acpi/acpica/rsio.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 364decc1028a..3461f7db26df 100644 --- a/drivers/acpi/acpica/rsirq.c +++ b/drivers/acpi/acpica/rsirq.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 6053aa182093..77291293af64 100644 --- a/drivers/acpi/acpica/rslist.c +++ b/drivers/acpi/acpica/rslist.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 ebc773a1b350..eab4483ff5f8 100644 --- a/drivers/acpi/acpica/rsmemory.c +++ b/drivers/acpi/acpica/rsmemory.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 c99cec9cefde..41eea4bc089c 100644 --- a/drivers/acpi/acpica/rsmisc.c +++ b/drivers/acpi/acpica/rsmisc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 fe49fc43e10f..9e8407223d95 100644 --- a/drivers/acpi/acpica/rsserial.c +++ b/drivers/acpi/acpica/rsserial.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 14a7982c9961..897a5ceb0420 100644 --- a/drivers/acpi/acpica/rsutils.c +++ b/drivers/acpi/acpica/rsutils.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 01e476988aae..877ab9202133 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 8f89263ac47e..ec14588254d4 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 e4f4f02d49e7..c12003947bd5 100644 --- a/drivers/acpi/acpica/tbfind.c +++ b/drivers/acpi/acpica/tbfind.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 634357d51fe9..e3040947e9a0 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -292,10 +292,11 @@ struct acpi_table_header *acpi_tb_table_override(struct acpi_table_header new_table = acpi_os_map_memory(new_address, new_table_length); if (!new_table) { ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY, - "%4.4s %p Attempted physical table override failed", + "%4.4s " ACPI_PRINTF_UINT + " Attempted physical table override failed", table_header->signature, - ACPI_CAST_PTR(void, - table_desc->address))); + ACPI_FORMAT_TO_UINT(table_desc-> + address))); return (NULL); } @@ -308,11 +309,11 @@ struct acpi_table_header *acpi_tb_table_override(struct acpi_table_header finish_override: - ACPI_INFO((AE_INFO, - "%4.4s %p %s table override, new table: %p", + ACPI_INFO((AE_INFO, "%4.4s " ACPI_PRINTF_UINT + " %s table override, new table: " ACPI_PRINTF_UINT, table_header->signature, - ACPI_CAST_PTR(void, table_desc->address), - override_type, new_table)); + ACPI_FORMAT_TO_UINT(table_desc->address), + override_type, ACPI_FORMAT_TO_UINT(new_table))); /* We can now unmap/delete the original table (if fully mapped) */ diff --git a/drivers/acpi/acpica/tbprint.c b/drivers/acpi/acpica/tbprint.c index 6866e767ba90..df3bb20ea325 100644 --- a/drivers/acpi/acpica/tbprint.c +++ b/drivers/acpi/acpica/tbprint.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -128,15 +128,17 @@ acpi_tb_print_table_header(acpi_physical_address address, struct acpi_table_header local_header; /* - * The reason that the Address is cast to a void pointer is so that we - * can use %p which will work properly on both 32-bit and 64-bit hosts. + * The reason that we use ACPI_PRINTF_UINT and ACPI_FORMAT_TO_UINT is to + * support both 32-bit and 64-bit hosts/addresses in a consistent manner. + * The %p specifier does not emit uniform output on all hosts. On some, + * leading zeros are not supported. */ if (ACPI_COMPARE_NAME(header->signature, ACPI_SIG_FACS)) { /* FACS only has signature and length fields */ - ACPI_INFO((AE_INFO, "%4.4s %p %06X", - header->signature, ACPI_CAST_PTR(void, address), + ACPI_INFO((AE_INFO, "%-4.4s " ACPI_PRINTF_UINT " %06X", + header->signature, ACPI_FORMAT_TO_UINT(address), header->length)); } else if (ACPI_VALIDATE_RSDP_SIG(header->signature)) { @@ -147,8 +149,9 @@ acpi_tb_print_table_header(acpi_physical_address address, header)->oem_id, ACPI_OEM_ID_SIZE); acpi_tb_fix_string(local_header.oem_id, ACPI_OEM_ID_SIZE); - ACPI_INFO((AE_INFO, "RSDP %p %06X (v%.2d %6.6s)", - ACPI_CAST_PTR(void, address), + ACPI_INFO((AE_INFO, + "RSDP " ACPI_PRINTF_UINT " %06X (v%.2d %-6.6s)", + ACPI_FORMAT_TO_UINT(address), (ACPI_CAST_PTR(struct acpi_table_rsdp, header)-> revision > 0) ? ACPI_CAST_PTR(struct acpi_table_rsdp, @@ -162,8 +165,9 @@ acpi_tb_print_table_header(acpi_physical_address address, acpi_tb_cleanup_table_header(&local_header, header); ACPI_INFO((AE_INFO, - "%4.4s %p %06X (v%.2d %6.6s %8.8s %08X %4.4s %08X)", - local_header.signature, ACPI_CAST_PTR(void, address), + "%-4.4s " ACPI_PRINTF_UINT + " %06X (v%.2d %-6.6s %-8.8s %08X %-4.4s %08X)", + local_header.signature, ACPI_FORMAT_TO_UINT(address), local_header.length, local_header.revision, local_header.oem_id, local_header.oem_table_id, local_header.oem_revision, diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index 6412d3c301cb..a4702eee91a8 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 db826eaadd1c..a1593159d9ea 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index 60b5a871833c..0909420fc776 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 e4e1468877c3..65ab8fed3d5e 100644 --- a/drivers/acpi/acpica/tbxfroot.c +++ b/drivers/acpi/acpica/tbxfroot.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 2c2b6ae5dfc4..a1acec9d2ef3 100644 --- a/drivers/acpi/acpica/utaddress.c +++ b/drivers/acpi/acpica/utaddress.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 1851762fc5b5..efac83c606dc 100644 --- a/drivers/acpi/acpica/utalloc.c +++ b/drivers/acpi/acpica/utalloc.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 11fde93be120..3c1699740653 100644 --- a/drivers/acpi/acpica/utbuffer.c +++ b/drivers/acpi/acpica/utbuffer.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 cacd2fd9e665..78fde0aac487 100644 --- a/drivers/acpi/acpica/utcache.c +++ b/drivers/acpi/acpica/utcache.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 edff4e653d9a..270c16464dd9 100644 --- a/drivers/acpi/acpica/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -535,10 +535,10 @@ acpi_ut_copy_esimple_to_isimple(union acpi_object *external_object, case ACPI_TYPE_LOCAL_REFERENCE: - /* TBD: should validate incoming handle */ + /* An incoming reference is defined to be a namespace node */ - internal_object->reference.class = ACPI_REFCLASS_NAME; - internal_object->reference.node = + internal_object->reference.class = ACPI_REFCLASS_REFOF; + internal_object->reference.object = external_object->reference.handle; break; diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index d971c8631263..21a20ac5b1e1 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 b3f31dd89a45..fbfa9eca011f 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 c07d2227ea42..a3516de213fa 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -75,6 +75,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) union acpi_operand_object *handler_desc; union acpi_operand_object *second_desc; union acpi_operand_object *next_desc; + union acpi_operand_object *start_desc; union acpi_operand_object **last_obj_ptr; ACPI_FUNCTION_TRACE_PTR(ut_delete_internal_obj, object); @@ -235,10 +236,11 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) if (handler_desc) { next_desc = handler_desc->address_space.region_list; + start_desc = next_desc; last_obj_ptr = &handler_desc->address_space.region_list; - /* Remove the region object from the handler's list */ + /* Remove the region object from the handler list */ while (next_desc) { if (next_desc == object) { @@ -247,10 +249,19 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) break; } - /* Walk the linked list of handler */ + /* Walk the linked list of handlers */ last_obj_ptr = &next_desc->region.next; next_desc = next_desc->region.next; + + /* Prevent infinite loop if list is corrupted */ + + if (next_desc == start_desc) { + ACPI_ERROR((AE_INFO, + "Circular region list in address handler object %p", + handler_desc)); + return_VOID; + } } if (handler_desc->address_space.handler_flags & diff --git a/drivers/acpi/acpica/uterror.c b/drivers/acpi/acpica/uterror.c index 154fdcaa5830..8e544d4688cd 100644 --- a/drivers/acpi/acpica/uterror.c +++ b/drivers/acpi/acpica/uterror.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 16fb90506db7..8fed1482d228 100644 --- a/drivers/acpi/acpica/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 3cf7b597edb9..0403dcaabaf2 100644 --- a/drivers/acpi/acpica/utexcep.c +++ b/drivers/acpi/acpica/utexcep.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 030cb0dc673c..f3abeae9d2f8 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,31 +55,27 @@ ACPI_MODULE_NAME("utglobal") * Static global variable initialization. * ******************************************************************************/ -/* - * We want the debug switches statically initialized so they - * are already set when the debugger is entered. - */ -/* Debug switch - level and trace mask */ +/* Debug output control masks */ u32 acpi_dbg_level = ACPI_DEBUG_DEFAULT; -/* Debug switch - layer (component) mask */ - u32 acpi_dbg_layer = 0; -u32 acpi_gbl_nesting_level = 0; -/* Debugger globals */ +/* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */ -u8 acpi_gbl_db_terminate_threads = FALSE; -u8 acpi_gbl_abort_method = FALSE; -u8 acpi_gbl_method_executing = FALSE; +struct acpi_table_fadt acpi_gbl_FADT; +u32 acpi_gbl_trace_flags; +acpi_name acpi_gbl_trace_method_name; +u8 acpi_gbl_system_awake_and_running; +u32 acpi_current_gpe_count; -/* System flags */ - -u32 acpi_gbl_startup_flags = 0; - -/* System starts uninitialized */ +/* + * ACPI 5.0 introduces the concept of a "reduced hardware platform", meaning + * that the ACPI hardware is no longer required. A flag in the FADT indicates + * a reduced HW machine, and that flag is duplicated here for convenience. + */ +u8 acpi_gbl_reduced_hardware; -u8 acpi_gbl_shutdown = TRUE; +/* Various state name strings */ const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT] = { "\\_S0_", @@ -335,7 +331,6 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_DSDT = NULL; acpi_gbl_cm_single_step = FALSE; - acpi_gbl_db_terminate_threads = FALSE; acpi_gbl_shutdown = FALSE; acpi_gbl_ns_lookup_count = 0; acpi_gbl_ps_find_count = 0; @@ -382,6 +377,10 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_disable_mem_tracking = FALSE; #endif +#ifdef ACPI_DEBUGGER + acpi_gbl_db_terminate_threads = FALSE; +#endif + return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c index bfca7b4b6731..4b12880e5b11 100644 --- a/drivers/acpi/acpica/utids.c +++ b/drivers/acpi/acpica/utids.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 c5d1ac44c07d..5f56fc49021e 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 5c26ad420344..dc6e96547f18 100644 --- a/drivers/acpi/acpica/utlock.c +++ b/drivers/acpi/acpica/utlock.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 909fe66e1934..d44dee6ee10a 100644 --- a/drivers/acpi/acpica/utmath.c +++ b/drivers/acpi/acpica/utmath.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 02f9101b65e4..2e2bb14e1099 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 08c323245584..82717fff9ffc 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 517af700399d..dfa9009bfc87 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 8856bd37bc76..685766fc6ca8 100644 --- a/drivers/acpi/acpica/utosi.c +++ b/drivers/acpi/acpica/utosi.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,6 +47,31 @@ #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utosi") +/****************************************************************************** + * + * ACPICA policy for new _OSI strings: + * + * It is the stated policy of ACPICA that new _OSI strings will be integrated + * into this module as soon as possible after they are defined. It is strongly + * recommended that all ACPICA hosts mirror this policy and integrate any + * changes to this module as soon as possible. There are several historical + * reasons behind this policy: + * + * 1) New BIOSs tend to test only the case where the host responds TRUE to + * the latest version of Windows, which would respond to the latest/newest + * _OSI string. Not responding TRUE to the latest version of Windows will + * risk executing untested code paths throughout the DSDT and SSDTs. + * + * 2) If a new _OSI string is recognized only after a significant delay, this + * has the potential to cause problems on existing working machines because + * of the possibility that a new and different path through the ASL code + * will be executed. + * + * 3) New _OSI strings are tending to come out about once per year. A delay + * in recognizing a new string for a significant amount of time risks the + * release of another string which only compounds the initial problem. + * + *****************************************************************************/ /* * Strings supported by the _OSI predefined control method (which is * implemented internally within this module.) @@ -74,6 +99,7 @@ static struct acpi_interface_info acpi_default_supported_interfaces[] = { {"Windows 2006 SP2", NULL, 0, ACPI_OSI_WIN_VISTA_SP2}, /* Windows Vista SP2 - Added 09/2010 */ {"Windows 2009", NULL, 0, ACPI_OSI_WIN_7}, /* Windows 7 and Server 2008 R2 - Added 09/2009 */ {"Windows 2012", NULL, 0, ACPI_OSI_WIN_8}, /* Windows 8 and Server 2012 - Added 08/2012 */ + {"Windows 2013", NULL, 0, ACPI_OSI_WIN_8}, /* Windows 8.1 and Server 2012 R2 - Added 01/2014 */ /* Feature Group Strings */ diff --git a/drivers/acpi/acpica/utownerid.c b/drivers/acpi/acpica/utownerid.c index eb3aca761369..36bec57ebd23 100644 --- a/drivers/acpi/acpica/utownerid.c +++ b/drivers/acpi/acpica/utownerid.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 2b1ce4cd3207..db30caff130a 100644 --- a/drivers/acpi/acpica/utpredef.c +++ b/drivers/acpi/acpica/utpredef.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 2c2accb9e534..14cb6c0c8be2 100644 --- a/drivers/acpi/acpica/utresrc.c +++ b/drivers/acpi/acpica/utresrc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,7 +47,8 @@ #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utresrc") -#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER) + +#if defined(ACPI_DEBUG_OUTPUT) || defined (ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER) /* * Strings used to decode resource descriptors. * Used by both the disassembler and the debugger resource dump routines diff --git a/drivers/acpi/acpica/utstate.c b/drivers/acpi/acpica/utstate.c index 03c4c2febd84..1cc97a752c15 100644 --- a/drivers/acpi/acpica/utstate.c +++ b/drivers/acpi/acpica/utstate.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 45c0eb26b33d..77219336c7e0 100644 --- a/drivers/acpi/acpica/utstring.c +++ b/drivers/acpi/acpica/utstring.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 c0027773cccb..7d0ee969d781 100644 --- a/drivers/acpi/acpica/uttrack.c +++ b/drivers/acpi/acpica/uttrack.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -276,7 +276,8 @@ acpi_ut_free_and_track(void *allocation, } acpi_os_free(debug_block); - ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "%p freed\n", allocation)); + ACPI_DEBUG_PRINT((ACPI_DB_ALLOCATIONS, "%p freed (block %p)\n", + allocation, debug_block)); return_VOID; } diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index be322c83643a..502a8492dc83 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 f7edb88f6054..edd861102f1b 100644 --- a/drivers/acpi/acpica/utxferror.c +++ b/drivers/acpi/acpica/utxferror.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 246ef68681f4..13380d818462 100644 --- a/drivers/acpi/acpica/utxfinit.c +++ b/drivers/acpi/acpica/utxfinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, 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 312299721ba1..2a0f9e04d3a4 100644 --- a/drivers/acpi/acpica/utxfmutex.c +++ b/drivers/acpi/acpica/utxfmutex.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2013, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index 3650b2183227..c4dac7150960 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig @@ -12,7 +12,7 @@ config ACPI_APEI config ACPI_APEI_GHES bool "APEI Generic Hardware Error Source" - depends on ACPI_APEI && X86 + depends on ACPI_APEI select ACPI_HED select IRQ_WORK select GENERIC_ALLOCATOR diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 797a6938d051..9a2c63b20050 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -39,15 +39,13 @@ #include <linux/acpi.h> #include <linux/power_supply.h> +#include "battery.h" + #define PREFIX "ACPI: " #define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF -#define ACPI_BATTERY_CLASS "battery" #define ACPI_BATTERY_DEVICE_NAME "Battery" -#define ACPI_BATTERY_NOTIFY_STATUS 0x80 -#define ACPI_BATTERY_NOTIFY_INFO 0x81 -#define ACPI_BATTERY_NOTIFY_THRESHOLD 0x82 /* Battery power unit: 0 means mW, 1 means mA */ #define ACPI_BATTERY_POWER_UNIT_MA 1 @@ -736,6 +734,7 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event) acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, acpi_battery_present(battery)); + acpi_notifier_call_chain(device, event, acpi_battery_present(battery)); /* acpi_battery_update could remove power_supply object */ if (old && battery->bat.dev) power_supply_changed(&battery->bat); diff --git a/drivers/acpi/battery.h b/drivers/acpi/battery.h new file mode 100644 index 000000000000..6c084976987d --- /dev/null +++ b/drivers/acpi/battery.h @@ -0,0 +1,10 @@ +#ifndef __ACPI_BATTERY_H +#define __ACPI_BATTERY_H + +#define ACPI_BATTERY_CLASS "battery" + +#define ACPI_BATTERY_NOTIFY_STATUS 0x80 +#define ACPI_BATTERY_NOTIFY_INFO 0x81 +#define ACPI_BATTERY_NOTIFY_THRESHOLD 0x82 + +#endif diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index fcb59c21c68d..e7e5844c87d0 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -311,9 +311,7 @@ static void acpi_bus_osc_support(void) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT; #endif -#ifdef ACPI_HOTPLUG_OST capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT; -#endif if (!ghes_disable) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT; @@ -340,60 +338,77 @@ static void acpi_bus_osc_support(void) */ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) { - struct acpi_device *device = NULL; + struct acpi_device *adev; struct acpi_driver *driver; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n", - type, handle)); + acpi_status status; + u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; switch (type) { - case ACPI_NOTIFY_BUS_CHECK: - /* TBD */ + acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); break; case ACPI_NOTIFY_DEVICE_CHECK: - /* TBD */ + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n"); break; case ACPI_NOTIFY_DEVICE_WAKE: - /* TBD */ + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_WAKE event\n"); break; case ACPI_NOTIFY_EJECT_REQUEST: - /* TBD */ + acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); break; case ACPI_NOTIFY_DEVICE_CHECK_LIGHT: + acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK_LIGHT event\n"); /* TBD: Exactly what does 'light' mean? */ break; case ACPI_NOTIFY_FREQUENCY_MISMATCH: - /* TBD */ + acpi_handle_err(handle, "Device cannot be configured due " + "to a frequency mismatch\n"); break; case ACPI_NOTIFY_BUS_MODE_MISMATCH: - /* TBD */ + acpi_handle_err(handle, "Device cannot be configured due " + "to a bus mode mismatch\n"); break; case ACPI_NOTIFY_POWER_FAULT: - /* TBD */ + acpi_handle_err(handle, "Device has suffered a power fault\n"); break; default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Received unknown/unsupported notification [%08x]\n", - type)); - break; + acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type); + ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY; + goto err; } - acpi_bus_get_device(handle, &device); - if (device) { - driver = device->driver; - if (driver && driver->ops.notify && - (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS)) - driver->ops.notify(device, type); + adev = acpi_bus_get_acpi_device(handle); + if (!adev) + goto err; + + driver = adev->driver; + if (driver && driver->ops.notify && + (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS)) + driver->ops.notify(adev, type); + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + case ACPI_NOTIFY_DEVICE_CHECK: + case ACPI_NOTIFY_EJECT_REQUEST: + status = acpi_hotplug_schedule(adev, type); + if (ACPI_SUCCESS(status)) + return; + default: + break; } + acpi_bus_put_acpi_device(adev); + return; + + err: + acpi_evaluate_ost(handle, type, ost_code, NULL); } /* -------------------------------------------------------------------------- diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 714e957a871a..db35594d4df7 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -302,6 +302,10 @@ static void acpi_button_notify(struct acpi_device *device, u32 event) input_sync(input); pm_wakeup_event(&device->dev, 0); + acpi_bus_generate_netlink_event( + device->pnp.device_class, + dev_name(&device->dev), + event, ++button->pushed); } break; default: diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 368f9ddb8480..63119d09b354 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -31,8 +31,6 @@ #include "internal.h" -#define PREFIX "ACPI: " - #define _COMPONENT ACPI_CONTAINER_COMPONENT ACPI_MODULE_NAME("container"); @@ -68,6 +66,9 @@ static int container_device_attach(struct acpi_device *adev, struct device *dev; int ret; + if (adev->flags.is_dock_station) + return 0; + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) return -ENOMEM; diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index c14a00d3dca6..d047739f3380 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -901,15 +901,30 @@ EXPORT_SYMBOL_GPL(acpi_dev_resume_early); int acpi_subsys_prepare(struct device *dev) { /* - * Follow PCI and resume devices suspended at run time before running - * their system suspend callbacks. + * Devices having power.ignore_children set may still be necessary for + * suspending their children in the next phase of device suspend. */ - pm_runtime_resume(dev); + if (dev->power.ignore_children) + pm_runtime_resume(dev); + return pm_generic_prepare(dev); } EXPORT_SYMBOL_GPL(acpi_subsys_prepare); /** + * acpi_subsys_suspend - Run the device driver's suspend callback. + * @dev: Device to handle. + * + * Follow PCI and resume devices suspended at run time before running their + * system suspend callbacks. + */ +int acpi_subsys_suspend(struct device *dev) +{ + pm_runtime_resume(dev); + return pm_generic_suspend(dev); +} + +/** * acpi_subsys_suspend_late - Suspend device using ACPI. * @dev: Device to suspend. * @@ -937,6 +952,23 @@ int acpi_subsys_resume_early(struct device *dev) return ret ? ret : pm_generic_resume_early(dev); } EXPORT_SYMBOL_GPL(acpi_subsys_resume_early); + +/** + * acpi_subsys_freeze - Run the device driver's freeze callback. + * @dev: Device to handle. + */ +int acpi_subsys_freeze(struct device *dev) +{ + /* + * This used to be done in acpi_subsys_prepare() for all devices and + * some drivers may depend on it, so do it here. Ideally, however, + * runtime-suspended devices should not be touched during freeze/thaw + * transitions. + */ + pm_runtime_resume(dev); + return pm_generic_freeze(dev); +} + #endif /* CONFIG_PM_SLEEP */ static struct dev_pm_domain acpi_general_pm_domain = { @@ -947,8 +979,11 @@ static struct dev_pm_domain acpi_general_pm_domain = { #endif #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, + .suspend = acpi_subsys_suspend, .suspend_late = acpi_subsys_suspend_late, .resume_early = acpi_subsys_resume_early, + .freeze = acpi_subsys_freeze, + .poweroff = acpi_subsys_suspend, .poweroff_late = acpi_subsys_suspend_late, .restore_early = acpi_subsys_resume_early, #endif diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 5bfd769fc91f..f0fc6260266b 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -1,7 +1,9 @@ /* * dock.c - ACPI dock station driver * - * Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com> + * Copyright (C) 2006, 2014, Intel Corp. + * Author: Kristen Carlson Accardi <kristen.c.accardi@intel.com> + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -35,8 +37,6 @@ #include "internal.h" -#define PREFIX "ACPI: " - #define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver" ACPI_MODULE_NAME("dock"); @@ -68,15 +68,10 @@ struct dock_station { }; static LIST_HEAD(dock_stations); static int dock_station_count; -static DEFINE_MUTEX(hotplug_lock); struct dock_dependent_device { struct list_head list; - acpi_handle handle; - const struct acpi_dock_ops *hp_ops; - void *hp_context; - unsigned int hp_refcount; - void (*hp_release)(void *); + struct acpi_device *adev; }; #define DOCK_DOCKING 0x00000001 @@ -98,13 +93,13 @@ enum dock_callback_type { *****************************************************************************/ /** * add_dock_dependent_device - associate a device with the dock station - * @ds: The dock station - * @handle: handle of the dependent device + * @ds: Dock station. + * @adev: Dependent ACPI device object. * * Add the dependent device to the dock's dependent device list. */ -static int __init -add_dock_dependent_device(struct dock_station *ds, acpi_handle handle) +static int add_dock_dependent_device(struct dock_station *ds, + struct acpi_device *adev) { struct dock_dependent_device *dd; @@ -112,180 +107,120 @@ add_dock_dependent_device(struct dock_station *ds, acpi_handle handle) if (!dd) return -ENOMEM; - dd->handle = handle; + dd->adev = adev; INIT_LIST_HEAD(&dd->list); list_add_tail(&dd->list, &ds->dependent_devices); return 0; } -static void remove_dock_dependent_devices(struct dock_station *ds) +static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, + enum dock_callback_type cb_type) { - struct dock_dependent_device *dd, *aux; + struct acpi_device *adev = dd->adev; - list_for_each_entry_safe(dd, aux, &ds->dependent_devices, list) { - list_del(&dd->list); - kfree(dd); - } -} + acpi_lock_hp_context(); -/** - * dock_init_hotplug - Initialize a hotplug device on a docking station. - * @dd: Dock-dependent device. - * @ops: Dock operations to attach to the dependent device. - * @context: Data to pass to the @ops callbacks and @release. - * @init: Optional initialization routine to run after setting up context. - * @release: Optional release routine to run on removal. - */ -static int dock_init_hotplug(struct dock_dependent_device *dd, - const struct acpi_dock_ops *ops, void *context, - void (*init)(void *), void (*release)(void *)) -{ - int ret = 0; + if (!adev->hp) + goto out; - mutex_lock(&hotplug_lock); - if (WARN_ON(dd->hp_context)) { - ret = -EEXIST; - } else { - dd->hp_refcount = 1; - dd->hp_ops = ops; - dd->hp_context = context; - dd->hp_release = release; - if (init) - init(context); - } - mutex_unlock(&hotplug_lock); - return ret; -} + if (cb_type == DOCK_CALL_FIXUP) { + void (*fixup)(struct acpi_device *); -/** - * dock_release_hotplug - Decrement hotplug reference counter of dock device. - * @dd: Dock-dependent device. - * - * Decrement the reference counter of @dd and if 0, detach its hotplug - * operations from it, reset its context pointer and run the optional release - * routine if present. - */ -static void dock_release_hotplug(struct dock_dependent_device *dd) -{ - mutex_lock(&hotplug_lock); - if (dd->hp_context && !--dd->hp_refcount) { - void (*release)(void *) = dd->hp_release; - void *context = dd->hp_context; - - dd->hp_ops = NULL; - dd->hp_context = NULL; - dd->hp_release = NULL; - if (release) - release(context); - } - mutex_unlock(&hotplug_lock); -} + fixup = adev->hp->fixup; + if (fixup) { + acpi_unlock_hp_context(); + fixup(adev); + return; + } + } else if (cb_type == DOCK_CALL_UEVENT) { + void (*uevent)(struct acpi_device *, u32); + + uevent = adev->hp->uevent; + if (uevent) { + acpi_unlock_hp_context(); + uevent(adev, event); + return; + } + } else { + int (*notify)(struct acpi_device *, u32); -static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, - enum dock_callback_type cb_type) -{ - acpi_notify_handler cb = NULL; - bool run = false; - - mutex_lock(&hotplug_lock); - - if (dd->hp_context) { - run = true; - dd->hp_refcount++; - if (dd->hp_ops) { - switch (cb_type) { - case DOCK_CALL_FIXUP: - cb = dd->hp_ops->fixup; - break; - case DOCK_CALL_UEVENT: - cb = dd->hp_ops->uevent; - break; - default: - cb = dd->hp_ops->handler; - } + notify = adev->hp->notify; + if (notify) { + acpi_unlock_hp_context(); + notify(adev, event); + return; } } - mutex_unlock(&hotplug_lock); + out: + acpi_unlock_hp_context(); +} - if (!run) - return; +static struct dock_station *find_dock_station(acpi_handle handle) +{ + struct dock_station *ds; - if (cb) - cb(dd->handle, event, dd->hp_context); + list_for_each_entry(ds, &dock_stations, sibling) + if (ds->handle == handle) + return ds; - dock_release_hotplug(dd); + return NULL; } /** * find_dock_dependent_device - get a device dependent on this dock * @ds: the dock station - * @handle: the acpi_handle of the device we want + * @adev: ACPI device object to find. * * iterate over the dependent device list for this dock. If the * dependent device matches the handle, return. */ static struct dock_dependent_device * -find_dock_dependent_device(struct dock_station *ds, acpi_handle handle) +find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev) { struct dock_dependent_device *dd; list_for_each_entry(dd, &ds->dependent_devices, list) - if (handle == dd->handle) + if (adev == dd->adev) return dd; return NULL; } -/***************************************************************************** - * Dock functions * - *****************************************************************************/ -static int __init is_battery(acpi_handle handle) +void register_dock_dependent_device(struct acpi_device *adev, + acpi_handle dshandle) { - struct acpi_device_info *info; - int ret = 1; + struct dock_station *ds = find_dock_station(dshandle); - if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info))) - return 0; - if (!(info->valid & ACPI_VALID_HID)) - ret = 0; - else - ret = !strcmp("PNP0C0A", info->hardware_id.string); - - kfree(info); - return ret; + if (ds && !find_dock_dependent_device(ds, adev)) + add_dock_dependent_device(ds, adev); } -/* Check whether ACPI object is an ejectable battery or disk bay */ -static bool __init is_ejectable_bay(acpi_handle handle) -{ - if (acpi_has_method(handle, "_EJ0") && is_battery(handle)) - return true; - - return acpi_bay_match(handle); -} +/***************************************************************************** + * Dock functions * + *****************************************************************************/ /** * is_dock_device - see if a device is on a dock station - * @handle: acpi handle of the device + * @adev: ACPI device object to check. * * If this device is either the dock station itself, * or is a device dependent on the dock station, then it * is a dock device */ -int is_dock_device(acpi_handle handle) +int is_dock_device(struct acpi_device *adev) { struct dock_station *dock_station; if (!dock_station_count) return 0; - if (acpi_dock_match(handle)) + if (acpi_dock_match(adev->handle)) return 1; list_for_each_entry(dock_station, &dock_stations, sibling) - if (find_dock_dependent_device(dock_station, handle)) + if (find_dock_dependent_device(dock_station, adev)) return 1; return 0; @@ -313,43 +248,6 @@ static int dock_present(struct dock_station *ds) } /** - * dock_create_acpi_device - add new devices to acpi - * @handle - handle of the device to add - * - * This function will create a new acpi_device for the given - * handle if one does not exist already. This should cause - * acpi to scan for drivers for the given devices, and call - * matching driver's add routine. - */ -static void dock_create_acpi_device(acpi_handle handle) -{ - struct acpi_device *device = NULL; - int ret; - - acpi_bus_get_device(handle, &device); - if (!acpi_device_enumerated(device)) { - ret = acpi_bus_scan(handle); - if (ret) - pr_debug("error adding bus, %x\n", -ret); - } -} - -/** - * dock_remove_acpi_device - remove the acpi_device struct from acpi - * @handle - the handle of the device to remove - * - * Tell acpi to remove the acpi_device. This should cause any loaded - * driver to have it's remove routine called. - */ -static void dock_remove_acpi_device(acpi_handle handle) -{ - struct acpi_device *device; - - if (!acpi_bus_get_device(handle, &device)) - acpi_bus_trim(device); -} - -/** * hot_remove_dock_devices - Remove dock station devices. * @ds: Dock station. */ @@ -366,7 +264,7 @@ static void hot_remove_dock_devices(struct dock_station *ds) dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false); list_for_each_entry_reverse(dd, &ds->dependent_devices, list) - dock_remove_acpi_device(dd->handle); + acpi_bus_trim(dd->adev); } /** @@ -392,12 +290,20 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) dock_hotplug_event(dd, event, DOCK_CALL_HANDLER); /* - * Now make sure that an acpi_device is created for each dependent - * device. That will cause scan handlers to be attached to device - * objects or acpi_drivers to be stopped/started if they are present. + * Check if all devices have been enumerated already. If not, run + * acpi_bus_scan() for them and that will cause scan handlers to be + * attached to device objects or acpi_drivers to be stopped/started if + * they are present. */ - list_for_each_entry(dd, &ds->dependent_devices, list) - dock_create_acpi_device(dd->handle); + list_for_each_entry(dd, &ds->dependent_devices, list) { + struct acpi_device *adev = dd->adev; + + if (!acpi_device_enumerated(adev)) { + int ret = acpi_bus_scan(adev->handle); + if (ret) + dev_dbg(&adev->dev, "scan error %d\n", -ret); + } + } } static void dock_event(struct dock_station *ds, u32 event, int num) @@ -501,71 +407,6 @@ static int dock_in_progress(struct dock_station *ds) } /** - * register_hotplug_dock_device - register a hotplug function - * @handle: the handle of the device - * @ops: handlers to call after docking - * @context: device specific data - * @init: Optional initialization routine to run after registration - * @release: Optional release routine to run on unregistration - * - * If a driver would like to perform a hotplug operation after a dock - * event, they can register an acpi_notifiy_handler to be called by - * the dock driver after _DCK is executed. - */ -int register_hotplug_dock_device(acpi_handle handle, - const struct acpi_dock_ops *ops, void *context, - void (*init)(void *), void (*release)(void *)) -{ - struct dock_dependent_device *dd; - struct dock_station *dock_station; - int ret = -EINVAL; - - if (WARN_ON(!context)) - return -EINVAL; - - if (!dock_station_count) - return -ENODEV; - - /* - * make sure this handle is for a device dependent on the dock, - * this would include the dock station itself - */ - list_for_each_entry(dock_station, &dock_stations, sibling) { - /* - * An ATA bay can be in a dock and itself can be ejected - * separately, so there are two 'dock stations' which need the - * ops - */ - dd = find_dock_dependent_device(dock_station, handle); - if (dd && !dock_init_hotplug(dd, ops, context, init, release)) - ret = 0; - } - - return ret; -} -EXPORT_SYMBOL_GPL(register_hotplug_dock_device); - -/** - * unregister_hotplug_dock_device - remove yourself from the hotplug list - * @handle: the acpi handle of the device - */ -void unregister_hotplug_dock_device(acpi_handle handle) -{ - struct dock_dependent_device *dd; - struct dock_station *dock_station; - - if (!dock_station_count) - return; - - list_for_each_entry(dock_station, &dock_stations, sibling) { - dd = find_dock_dependent_device(dock_station, handle); - if (dd) - dock_release_hotplug(dd); - } -} -EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); - -/** * handle_eject_request - handle an undock request checking for error conditions * * Check to make sure the dock device is still present, then undock and @@ -598,20 +439,23 @@ static int handle_eject_request(struct dock_station *ds, u32 event) } /** - * dock_notify - act upon an acpi dock notification - * @ds: dock station - * @event: the acpi event + * dock_notify - Handle ACPI dock notification. + * @adev: Dock station's ACPI device object. + * @event: Event code. * * If we are notified to dock, then check to see if the dock is * present and then dock. Notify all drivers of the dock event, * and then hotplug and devices that may need hotplugging. */ -static void dock_notify(struct dock_station *ds, u32 event) +int dock_notify(struct acpi_device *adev, u32 event) { - acpi_handle handle = ds->handle; - struct acpi_device *adev = NULL; + acpi_handle handle = adev->handle; + struct dock_station *ds = find_dock_station(handle); int surprise_removal = 0; + if (!ds) + return -ENODEV; + /* * According to acpi spec 3.0a, if a DEVICE_CHECK notification * is sent and _DCK is present, it is assumed to mean an undock @@ -632,7 +476,6 @@ static void dock_notify(struct dock_station *ds, u32 event) switch (event) { case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: - acpi_bus_get_device(handle, &adev); if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) { begin_dock(ds); dock(ds); @@ -662,49 +505,8 @@ static void dock_notify(struct dock_station *ds, u32 event) else dock_event(ds, event, UNDOCK_EVENT); break; - default: - acpi_handle_err(handle, "Unknown dock event %d\n", event); } -} - -static void acpi_dock_deferred_cb(void *data, u32 event) -{ - acpi_scan_lock_acquire(); - dock_notify(data, event); - acpi_scan_lock_release(); -} - -static void dock_notify_handler(acpi_handle handle, u32 event, void *data) -{ - if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK - && event != ACPI_NOTIFY_EJECT_REQUEST) - return; - - acpi_hotplug_execute(acpi_dock_deferred_cb, data, event); -} - -/** - * find_dock_devices - find devices on the dock station - * @handle: the handle of the device we are examining - * @lvl: unused - * @context: the dock station private data - * @rv: unused - * - * This function is called by acpi_walk_namespace. It will - * check to see if an object has an _EJD method. If it does, then it - * will see if it is dependent on the dock station. - */ -static acpi_status __init find_dock_devices(acpi_handle handle, u32 lvl, - void *context, void **rv) -{ - struct dock_station *ds = context; - acpi_handle ejd = NULL; - - acpi_bus_get_ejd(handle, &ejd); - if (ejd == ds->handle) - add_dock_dependent_device(ds, handle); - - return AE_OK; + return 0; } /* @@ -803,23 +605,28 @@ static struct attribute_group dock_attribute_group = { }; /** - * dock_add - add a new dock station - * @handle: the dock station handle + * acpi_dock_add - Add a new dock station + * @adev: Dock station ACPI device object. * - * allocated and initialize a new dock station device. Find all devices - * that are on the dock station, and register for dock event notifications. + * allocated and initialize a new dock station device. */ -static int __init dock_add(acpi_handle handle) +void acpi_dock_add(struct acpi_device *adev) { struct dock_station *dock_station, ds = { NULL, }; + struct platform_device_info pdevinfo; + acpi_handle handle = adev->handle; struct platform_device *dd; - acpi_status status; int ret; - dd = platform_device_register_data(NULL, "dock", dock_station_count, - &ds, sizeof(ds)); + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.name = "dock"; + pdevinfo.id = dock_station_count; + pdevinfo.acpi_node.companion = adev; + pdevinfo.data = &ds; + pdevinfo.size_data = sizeof(ds); + dd = platform_device_register_full(&pdevinfo); if (IS_ERR(dd)) - return PTR_ERR(dd); + return; dock_station = dd->dev.platform_data; @@ -837,72 +644,29 @@ static int __init dock_add(acpi_handle handle) dock_station->flags |= DOCK_IS_DOCK; if (acpi_ata_match(handle)) dock_station->flags |= DOCK_IS_ATA; - if (is_battery(handle)) + if (acpi_device_is_battery(adev)) dock_station->flags |= DOCK_IS_BAT; ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group); if (ret) goto err_unregister; - /* Find dependent devices */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_dock_devices, NULL, - dock_station, NULL); - /* add the dock station as a device dependent on itself */ - ret = add_dock_dependent_device(dock_station, handle); + ret = add_dock_dependent_device(dock_station, adev); if (ret) goto err_rmgroup; - status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - dock_notify_handler, dock_station); - if (ACPI_FAILURE(status)) { - ret = -ENODEV; - goto err_rmgroup; - } - dock_station_count++; list_add(&dock_station->sibling, &dock_stations); - return 0; + adev->flags.is_dock_station = true; + dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n", + dock_station_count); + return; err_rmgroup: - remove_dock_dependent_devices(dock_station); sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group); + err_unregister: platform_device_unregister(dd); acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret); - return ret; -} - -/** - * find_dock_and_bay - look for dock stations and bays - * @handle: acpi handle of a device - * @lvl: unused - * @context: unused - * @rv: unused - * - * This is called by acpi_walk_namespace to look for dock stations and bays. - */ -static acpi_status __init -find_dock_and_bay(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - if (acpi_dock_match(handle) || is_ejectable_bay(handle)) - dock_add(handle); - - return AE_OK; -} - -void __init acpi_dock_init(void) -{ - /* look for dock stations and bays */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_dock_and_bay, NULL, NULL, NULL); - - if (!dock_station_count) { - pr_info(PREFIX "No dock devices found.\n"); - return; - } - - pr_info(PREFIX "%s: %d docks/bays found\n", - ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count); } diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 959d41acc108..d7d32c28829b 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -67,6 +67,8 @@ 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_CLEAR_MAX 100 /* Maximum number of events to query + * when trying to clear the EC */ enum { EC_FLAGS_QUERY_PENDING, /* Query is pending */ @@ -116,6 +118,7 @@ EXPORT_SYMBOL(first_ec); static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ +static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ /* -------------------------------------------------------------------------- Transaction Management @@ -440,6 +443,29 @@ acpi_handle ec_get_handle(void) EXPORT_SYMBOL(ec_get_handle); +static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 *data); + +/* + * Clears stale _Q events that might have accumulated in the EC. + * Run with locked ec mutex. + */ +static void acpi_ec_clear(struct acpi_ec *ec) +{ + int i, status; + u8 value = 0; + + for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) { + status = acpi_ec_query_unlocked(ec, &value); + if (status || !value) + break; + } + + if (unlikely(i == ACPI_EC_CLEAR_MAX)) + pr_warn("Warning: Maximum of %d stale EC events cleared\n", i); + else + pr_info("%d stale EC events cleared\n", i); +} + void acpi_ec_block_transactions(void) { struct acpi_ec *ec = first_ec; @@ -463,6 +489,10 @@ void acpi_ec_unblock_transactions(void) mutex_lock(&ec->mutex); /* Allow transactions to be carried out again */ clear_bit(EC_FLAGS_BLOCKED, &ec->flags); + + if (EC_FLAGS_CLEAR_ON_RESUME) + acpi_ec_clear(ec); + mutex_unlock(&ec->mutex); } @@ -821,6 +851,13 @@ static int acpi_ec_add(struct acpi_device *device) /* EC is fully operational, allow queries */ clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); + + /* Clear stale _Q events if hardware might require that */ + if (EC_FLAGS_CLEAR_ON_RESUME) { + mutex_lock(&ec->mutex); + acpi_ec_clear(ec); + mutex_unlock(&ec->mutex); + } return ret; } @@ -922,6 +959,30 @@ static int ec_enlarge_storm_threshold(const struct dmi_system_id *id) return 0; } +/* + * On some hardware it is necessary to clear events accumulated by the EC during + * sleep. These ECs stop reporting GPEs until they are manually polled, if too + * many events are accumulated. (e.g. Samsung Series 5/9 notebooks) + * + * https://bugzilla.kernel.org/show_bug.cgi?id=44161 + * + * Ideally, the EC should also be instructed NOT to accumulate events during + * sleep (which Windows seems to do somehow), but the interface to control this + * behaviour is not known at this time. + * + * Models known to be affected are Samsung 530Uxx/535Uxx/540Uxx/550Pxx/900Xxx, + * however it is very likely that other Samsung models are affected. + * + * On systems which don't accumulate _Q events during sleep, this extra check + * should be harmless. + */ +static int ec_clear_on_resume(const struct dmi_system_id *id) +{ + pr_debug("Detected system needing EC poll on resume.\n"); + EC_FLAGS_CLEAR_ON_RESUME = 1; + return 0; +} + static struct dmi_system_id ec_dmi_table[] __initdata = { { ec_skip_dsdt_scan, "Compal JFL92", { @@ -965,6 +1026,9 @@ static struct dmi_system_id ec_dmi_table[] __initdata = { ec_validate_ecdt, "ASUS hardware", { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek Computer Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "L4R"),}, NULL}, + { + ec_clear_on_resume, "Samsung hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL}, {}, }; diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 09e423f3d8ad..8acf53e62966 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -55,11 +55,16 @@ MODULE_DEVICE_TABLE(acpi, fan_device_ids); #ifdef CONFIG_PM_SLEEP static int acpi_fan_suspend(struct device *dev); static int acpi_fan_resume(struct device *dev); +static struct dev_pm_ops acpi_fan_pm = { + .resume = acpi_fan_resume, + .freeze = acpi_fan_suspend, + .thaw = acpi_fan_resume, + .restore = acpi_fan_resume, +}; +#define FAN_PM_OPS_PTR (&acpi_fan_pm) #else -#define acpi_fan_suspend NULL -#define acpi_fan_resume NULL +#define FAN_PM_OPS_PTR NULL #endif -static SIMPLE_DEV_PM_OPS(acpi_fan_pm, acpi_fan_suspend, acpi_fan_resume); static struct acpi_driver acpi_fan_driver = { .name = "fan", @@ -69,7 +74,7 @@ static struct acpi_driver acpi_fan_driver = { .add = acpi_fan_add, .remove = acpi_fan_remove, }, - .drv.pm = &acpi_fan_pm, + .drv.pm = FAN_PM_OPS_PTR, }; /* thermal cooling device callbacks */ diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 0c789224d40d..f774c65ecb8b 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -287,6 +287,7 @@ EXPORT_SYMBOL_GPL(acpi_unbind_one); static int acpi_platform_notify(struct device *dev) { struct acpi_bus_type *type = acpi_get_bus_type(dev); + struct acpi_device *adev; int ret; ret = acpi_bind_one(dev, NULL); @@ -303,9 +304,14 @@ static int acpi_platform_notify(struct device *dev) if (ret) goto out; } + adev = ACPI_COMPANION(dev); + if (!adev) + goto out; if (type && type->setup) type->setup(dev); + else if (adev->handler && adev->handler->bind) + adev->handler->bind(dev); out: #if ACPI_GLUE_DEBUG @@ -324,11 +330,17 @@ static int acpi_platform_notify(struct device *dev) static int acpi_platform_notify_remove(struct device *dev) { + struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_bus_type *type; + if (!adev) + return 0; + type = acpi_get_bus_type(dev); if (type && type->cleanup) type->cleanup(dev); + else if (adev->handler && adev->handler->unbind) + adev->handler->unbind(dev); acpi_unbind_one(dev); return 0; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index dedbb2d802f1..957391306cbf 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -37,9 +37,15 @@ void acpi_container_init(void); static inline void acpi_container_init(void) {} #endif #ifdef CONFIG_ACPI_DOCK -void acpi_dock_init(void); +void register_dock_dependent_device(struct acpi_device *adev, + acpi_handle dshandle); +int dock_notify(struct acpi_device *adev, u32 event); +void acpi_dock_add(struct acpi_device *adev); #else -static inline void acpi_dock_init(void) {} +static inline void register_dock_dependent_device(struct acpi_device *adev, + acpi_handle dshandle) {} +static inline int dock_notify(struct acpi_device *adev, u32 event) { return -ENODEV; } +static inline void acpi_dock_add(struct acpi_device *adev) {} #endif #ifdef CONFIG_ACPI_HOTPLUG_MEMORY void acpi_memory_hotplug_init(void); @@ -72,7 +78,9 @@ void acpi_lpss_init(void); static inline void acpi_lpss_init(void) {} #endif +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); bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); /* -------------------------------------------------------------------------- @@ -90,6 +98,7 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); int acpi_bind_one(struct device *dev, struct acpi_device *adev); int acpi_unbind_one(struct device *dev); bool acpi_device_is_present(struct acpi_device *adev); +bool acpi_device_is_battery(struct acpi_device *adev); /* -------------------------------------------------------------------------- Power Resource diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 9e6816ef280a..24b5476449a1 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -60,7 +60,7 @@ int node_to_pxm(int node) return node_to_pxm_map[node]; } -void __acpi_map_pxm_to_node(int pxm, int node) +static void __acpi_map_pxm_to_node(int pxm, int node) { if (pxm_to_node_map[pxm] == NUMA_NO_NODE || node < pxm_to_node_map[pxm]) pxm_to_node_map[pxm] = node; @@ -193,7 +193,7 @@ static int __init acpi_parse_slit(struct acpi_table_header *table) return 0; } -void __init __attribute__ ((weak)) +void __init __weak acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) { printk(KERN_WARNING PREFIX @@ -314,7 +314,7 @@ int __init acpi_numa_init(void) return 0; } -int acpi_get_pxm(acpi_handle h) +static int acpi_get_pxm(acpi_handle h) { unsigned long long pxm; acpi_status status; @@ -331,14 +331,14 @@ int acpi_get_pxm(acpi_handle h) return -1; } -int acpi_get_node(acpi_handle *handle) +int acpi_get_node(acpi_handle handle) { - int pxm, node = NUMA_NO_NODE; + int pxm; pxm = acpi_get_pxm(handle); - if (pxm >= 0 && pxm < MAX_PXM_DOMAINS) - node = acpi_map_pxm_to_node(pxm); + if (pxm < 0 || pxm >= MAX_PXM_DOMAINS) + return NUMA_NO_NODE; - return node; + return acpi_map_pxm_to_node(pxm); } EXPORT_SYMBOL(acpi_get_node); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index fc1aa7909690..27f84af4e337 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -52,7 +52,7 @@ #define _COMPONENT ACPI_OS_SERVICES ACPI_MODULE_NAME("osl"); -#define PREFIX "ACPI: " + struct acpi_os_dpc { acpi_osd_exec_callback function; void *context; @@ -1168,8 +1168,7 @@ void acpi_os_wait_events_complete(void) struct acpi_hp_work { struct work_struct work; - acpi_hp_callback func; - void *data; + struct acpi_device *adev; u32 src; }; @@ -1178,25 +1177,24 @@ static void acpi_hotplug_work_fn(struct work_struct *work) struct acpi_hp_work *hpw = container_of(work, struct acpi_hp_work, work); acpi_os_wait_events_complete(); - hpw->func(hpw->data, hpw->src); + acpi_device_hotplug(hpw->adev, hpw->src); kfree(hpw); } -acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src) +acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src) { struct acpi_hp_work *hpw; ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "Scheduling function [%p(%p, %u)] for deferred execution.\n", - func, data, src)); + "Scheduling hotplug event (%p, %u) for deferred execution.\n", + adev, src)); hpw = kmalloc(sizeof(*hpw), GFP_KERNEL); if (!hpw) return AE_NO_MEMORY; INIT_WORK(&hpw->work, acpi_hotplug_work_fn); - hpw->func = func; - hpw->data = data; + hpw->adev = adev; hpw->src = src; /* * We can't run hotplug code in kacpid_wq/kacpid_notify_wq etc., because @@ -1780,6 +1778,17 @@ static int __init acpi_no_auto_ssdt_setup(char *s) __setup("acpi_no_auto_ssdt", acpi_no_auto_ssdt_setup); +static int __init acpi_disable_return_repair(char *s) +{ + printk(KERN_NOTICE PREFIX + "ACPI: Predefined validation mechanism disabled\n"); + acpi_gbl_disable_auto_repair = TRUE; + + return 1; +} + +__setup("acpica_no_return_repair", acpi_disable_return_repair); + acpi_status __init acpi_os_initialize(void) { acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1a_event_block); diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 361b40c10c3f..9c62340c2360 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -370,6 +370,30 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) return NULL; } +#if IS_ENABLED(CONFIG_ISA) || IS_ENABLED(CONFIG_EISA) +static int acpi_isa_register_gsi(struct pci_dev *dev) +{ + u32 dev_gsi; + + /* Interrupt Line values above 0xF are forbidden */ + if (dev->irq > 0 && (dev->irq <= 0xF) && + (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) { + dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n", + pin_name(dev->pin), dev->irq); + acpi_register_gsi(&dev->dev, dev_gsi, + ACPI_LEVEL_SENSITIVE, + ACPI_ACTIVE_LOW); + return 0; + } + return -EINVAL; +} +#else +static inline int acpi_isa_register_gsi(struct pci_dev *dev) +{ + return -ENODEV; +} +#endif + int acpi_pci_irq_enable(struct pci_dev *dev) { struct acpi_prt_entry *entry; @@ -416,19 +440,9 @@ int acpi_pci_irq_enable(struct pci_dev *dev) * driver reported one, then use it. Exit in any case. */ if (gsi < 0) { - u32 dev_gsi; - /* Interrupt Line values above 0xF are forbidden */ - if (dev->irq > 0 && (dev->irq <= 0xF) && - (acpi_isa_irq_to_gsi(dev->irq, &dev_gsi) == 0)) { - dev_warn(&dev->dev, "PCI INT %c: no GSI - using ISA IRQ %d\n", - pin_name(pin), dev->irq); - acpi_register_gsi(&dev->dev, dev_gsi, - ACPI_LEVEL_SENSITIVE, - ACPI_ACTIVE_LOW); - } else { + if (acpi_isa_register_gsi(dev)) dev_warn(&dev->dev, "PCI INT %c: no GSI\n", pin_name(pin)); - } kfree(entry); return 0; diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 9418c7a1f786..cfd7581cc19f 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -43,8 +43,6 @@ #include "internal.h" -#define PREFIX "ACPI: " - #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_link"); #define ACPI_PCI_LINK_CLASS "pci_irq_routing" diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index c1c4102e6478..d388f13d48b4 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -39,8 +39,6 @@ #include "internal.h" -#define PREFIX "ACPI: " - #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_root"); #define ACPI_PCI_ROOT_CLASS "pci_bridge" @@ -51,7 +49,7 @@ static void acpi_pci_root_remove(struct acpi_device *device); static int acpi_pci_root_scan_dependent(struct acpi_device *adev) { - acpiphp_check_host_bridge(adev->handle); + acpiphp_check_host_bridge(adev); return 0; } diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index ad7da686e6e6..e0bcfb642b52 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -46,8 +46,6 @@ #include "sleep.h" #include "internal.h" -#define PREFIX "ACPI: " - #define _COMPONENT ACPI_POWER_COMPONENT ACPI_MODULE_NAME("power"); #define ACPI_POWER_CLASS "power_resource" diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index a4eea9a508d3..86d73d5d503f 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -15,28 +15,9 @@ #include "internal.h" -#define PREFIX "ACPI: " #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_core"); -static int __init set_no_mwait(const struct dmi_system_id *id) -{ - printk(KERN_NOTICE PREFIX "%s detected - " - "disabling mwait for CPU C-states\n", id->ident); - boot_option_idle_override = IDLE_NOMWAIT; - return 0; -} - -static struct dmi_system_id processor_idle_dmi_table[] __initdata = { - { - set_no_mwait, "Extensa 5220", { - DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"), - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), - DMI_MATCH(DMI_BOARD_NAME, "Columbia") }, NULL}, - {}, -}; - static int map_lapic_id(struct acpi_subtable_header *entry, u32 acpi_id, int *apic_id) { @@ -89,6 +70,28 @@ static int map_lsapic_id(struct acpi_subtable_header *entry, return 0; } +static int map_gic_id(struct acpi_subtable_header *entry, + int device_declaration, u32 acpi_id, int *apic_id) +{ + struct acpi_madt_generic_interrupt *gic = + (struct acpi_madt_generic_interrupt *)entry; + + if (!(gic->flags & ACPI_MADT_ENABLED)) + return -ENODEV; + + /* + * In the GIC interrupt model, logical processors are + * required to have a Processor Device object in the DSDT, + * so we should check device_declaration here + */ + if (device_declaration && (gic->uid == acpi_id)) { + *apic_id = gic->gic_id; + return 0; + } + + return -EINVAL; +} + static int map_madt_entry(int type, u32 acpi_id) { unsigned long madt_end, entry; @@ -124,6 +127,9 @@ static int map_madt_entry(int type, u32 acpi_id) } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { if (!map_lsapic_id(header, type, acpi_id, &apic_id)) break; + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + if (!map_gic_id(header, type, acpi_id, &apic_id)) + break; } entry += header->length; } @@ -154,6 +160,8 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) map_lapic_id(header, acpi_id, &apic_id); } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { map_lsapic_id(header, type, acpi_id, &apic_id); + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + map_gic_id(header, type, acpi_id, &apic_id); } exit: @@ -323,7 +331,7 @@ static struct acpi_object_list *acpi_processor_alloc_pdc(void) * _PDC is required for a BIOS-OS handshake for most of the newer * ACPI processor features. */ -static int +static acpi_status acpi_processor_eval_pdc(acpi_handle handle, struct acpi_object_list *pdc_in) { acpi_status status = AE_OK; @@ -379,16 +387,43 @@ early_init_pdc(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; } -void __init acpi_early_processor_set_pdc(void) +#if defined(CONFIG_X86) || defined(CONFIG_IA64) +static int __init set_no_mwait(const struct dmi_system_id *id) +{ + pr_notice(PREFIX "%s detected - disabling mwait for CPU C-states\n", + id->ident); + boot_option_idle_override = IDLE_NOMWAIT; + return 0; +} + +static struct dmi_system_id processor_idle_dmi_table[] __initdata = { + { + set_no_mwait, "Extensa 5220", { + DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"), + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), + DMI_MATCH(DMI_BOARD_NAME, "Columbia") }, NULL}, + {}, +}; + +static void __init processor_dmi_check(void) { /* * Check whether the system is DMI table. If yes, OSPM * should not use mwait for CPU-states. */ dmi_check_system(processor_idle_dmi_table); +} +#else +static inline void processor_dmi_check(void) {} +#endif + +void __init acpi_early_processor_set_pdc(void) +{ + processor_dmi_check(); acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, early_init_pdc, NULL, NULL, NULL); - acpi_get_devices("ACPI0007", early_init_pdc, NULL, NULL); + acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID, early_init_pdc, NULL, NULL); } diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index c1c35623550f..7f70f3182d50 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -41,8 +41,6 @@ #include "internal.h" -#define PREFIX "ACPI: " - #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 #define ACPI_PROCESSOR_NOTIFY_POWER 0x81 #define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82 diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index ff90054f04fd..cfc8aba72f86 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -156,17 +156,9 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr) */ static void acpi_processor_ppc_ost(acpi_handle handle, int status) { - union acpi_object params[2] = { - {.type = ACPI_TYPE_INTEGER,}, - {.type = ACPI_TYPE_INTEGER,}, - }; - struct acpi_object_list arg_list = {2, params}; - - if (acpi_has_method(handle, "_OST")) { - params[0].integer.value = ACPI_PROCESSOR_NOTIFY_PERFORMANCE; - params[1].integer.value = status; - acpi_evaluate_object(handle, "_OST", &arg_list, NULL); - } + if (acpi_has_method(handle, "_OST")) + acpi_evaluate_ost(handle, ACPI_PROCESSOR_NOTIFY_PERFORMANCE, + status, NULL); } int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index b7201fc6f1e1..0bdacc5e26a3 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -77,18 +77,24 @@ 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->address_length) + return false; acpi_dev_get_memresource(res, memory24->minimum, memory24->address_length, memory24->write_protect); break; case ACPI_RESOURCE_TYPE_MEMORY32: memory32 = &ares->data.memory32; + if (!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_length) + return false; acpi_dev_get_memresource(res, fixed_memory32->address, fixed_memory32->address_length, fixed_memory32->write_protect); @@ -144,12 +150,16 @@ 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->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_length) + return false; acpi_dev_get_ioresource(res, fixed_io->address, fixed_io->address_length, ACPI_DECODE_10); diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index dbd48498b938..366ca40a6f70 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -37,12 +37,12 @@ #include <linux/power_supply.h> #include "sbshc.h" +#include "battery.h" #define PREFIX "ACPI: " #define ACPI_SBS_CLASS "sbs" #define ACPI_AC_CLASS "ac_adapter" -#define ACPI_BATTERY_CLASS "battery" #define ACPI_SBS_DEVICE_NAME "Smart Battery System" #define ACPI_SBS_FILE_INFO "info" #define ACPI_SBS_FILE_STATE "state" diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 57b053f424d1..7efe546a8c42 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -41,6 +41,7 @@ static DEFINE_MUTEX(acpi_scan_lock); static LIST_HEAD(acpi_scan_handlers_list); DEFINE_MUTEX(acpi_device_lock); LIST_HEAD(acpi_wakeup_device_list); +static DEFINE_MUTEX(acpi_hp_context_lock); struct acpi_device_bus_id{ char bus_id[15]; @@ -60,6 +61,27 @@ void acpi_scan_lock_release(void) } EXPORT_SYMBOL_GPL(acpi_scan_lock_release); +void acpi_lock_hp_context(void) +{ + mutex_lock(&acpi_hp_context_lock); +} + +void acpi_unlock_hp_context(void) +{ + mutex_unlock(&acpi_hp_context_lock); +} + +void acpi_initialize_hp_context(struct acpi_device *adev, + struct acpi_hotplug_context *hp, + int (*notify)(struct acpi_device *, u32), + void (*uevent)(struct acpi_device *, u32)) +{ + acpi_lock_hp_context(); + acpi_set_hp_context(adev, hp, notify, uevent, NULL); + acpi_unlock_hp_context(); +} +EXPORT_SYMBOL_GPL(acpi_initialize_hp_context); + int acpi_scan_add_handler(struct acpi_scan_handler *handler) { if (!handler || !handler->attach) @@ -439,90 +461,75 @@ static int acpi_scan_bus_check(struct acpi_device *adev) return 0; } -static void acpi_device_hotplug(void *data, u32 src) +static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type) { - u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; - struct acpi_device *adev = data; - int error; - - lock_device_hotplug(); - mutex_lock(&acpi_scan_lock); - - /* - * The device object's ACPI handle cannot become invalid as long as we - * are holding acpi_scan_lock, but it may have become invalid before - * that lock was acquired. - */ - if (adev->handle == INVALID_ACPI_HANDLE) - goto out; - - switch (src) { + switch (type) { case ACPI_NOTIFY_BUS_CHECK: - error = acpi_scan_bus_check(adev); - break; + return acpi_scan_bus_check(adev); case ACPI_NOTIFY_DEVICE_CHECK: - error = acpi_scan_device_check(adev); - break; + return acpi_scan_device_check(adev); case ACPI_NOTIFY_EJECT_REQUEST: case ACPI_OST_EC_OSPM_EJECT: - error = acpi_scan_hot_remove(adev); - break; - default: - error = -EINVAL; - break; + if (adev->handler && !adev->handler->hotplug.enabled) { + dev_info(&adev->dev, "Eject disabled\n"); + return -EPERM; + } + acpi_evaluate_ost(adev->handle, ACPI_NOTIFY_EJECT_REQUEST, + ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); + return acpi_scan_hot_remove(adev); } - if (!error) - ost_code = ACPI_OST_SC_SUCCESS; - - out: - acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL); - put_device(&adev->dev); - mutex_unlock(&acpi_scan_lock); - unlock_device_hotplug(); + return -EINVAL; } -static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) +void acpi_device_hotplug(struct acpi_device *adev, u32 src) { u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; - struct acpi_device *adev; - acpi_status status; + int error = -ENODEV; - if (acpi_bus_get_device(handle, &adev)) - goto err_out; + lock_device_hotplug(); + mutex_lock(&acpi_scan_lock); - switch (type) { - case ACPI_NOTIFY_BUS_CHECK: - acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); - break; - case ACPI_NOTIFY_DEVICE_CHECK: - acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n"); - break; - case ACPI_NOTIFY_EJECT_REQUEST: - acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); - if (!adev->handler) - goto err_out; + /* + * The device object's ACPI handle cannot become invalid as long as we + * are holding acpi_scan_lock, but it might have become invalid before + * that lock was acquired. + */ + if (adev->handle == INVALID_ACPI_HANDLE) + goto err_out; - if (!adev->handler->hotplug.enabled) { - acpi_handle_err(handle, "Eject disabled\n"); + if (adev->flags.is_dock_station) { + error = dock_notify(adev, src); + } else if (adev->flags.hotplug_notify) { + error = acpi_generic_hotplug_event(adev, src); + if (error == -EPERM) { ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; goto err_out; } - acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, - ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); - break; - default: - /* non-hotplug event; possibly handled by other handler */ - return; - } - get_device(&adev->dev); - status = acpi_hotplug_execute(acpi_device_hotplug, adev, type); - if (ACPI_SUCCESS(status)) - return; + } else { + int (*notify)(struct acpi_device *, u32); - put_device(&adev->dev); + acpi_lock_hp_context(); + notify = adev->hp ? adev->hp->notify : NULL; + acpi_unlock_hp_context(); + /* + * There may be additional notify handlers for device objects + * without the .event() callback, so ignore them here. + */ + if (notify) + error = notify(adev, src); + else + goto out; + } + if (!error) + ost_code = ACPI_OST_SC_SUCCESS; err_out: - acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); + acpi_evaluate_ost(adev->handle, src, ost_code, NULL); + + out: + acpi_bus_put_acpi_device(adev); + mutex_unlock(&acpi_scan_lock); + unlock_device_hotplug(); } static ssize_t real_power_state_show(struct device *dev, @@ -570,17 +577,14 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable) return -ENODEV; - acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, - ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); get_device(&acpi_device->dev); - status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device, - ACPI_OST_EC_OSPM_EJECT); + status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT); if (ACPI_SUCCESS(status)) return count; put_device(&acpi_device->dev); - acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, - ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); + acpi_evaluate_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, + ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN; } @@ -1114,14 +1118,16 @@ static void acpi_scan_drop_device(acpi_handle handle, void *context) mutex_unlock(&acpi_device_del_lock); } -int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) +static int acpi_get_device_data(acpi_handle handle, struct acpi_device **device, + void (*callback)(void *)) { acpi_status status; if (!device) return -EINVAL; - status = acpi_get_data(handle, acpi_scan_drop_device, (void **)device); + status = acpi_get_data_full(handle, acpi_scan_drop_device, + (void **)device, callback); if (ACPI_FAILURE(status) || !*device) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", handle)); @@ -1129,8 +1135,32 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) } return 0; } + +int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) +{ + return acpi_get_device_data(handle, device, NULL); +} EXPORT_SYMBOL(acpi_bus_get_device); +static void get_acpi_device(void *dev) +{ + if (dev) + get_device(&((struct acpi_device *)dev)->dev); +} + +struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle) +{ + struct acpi_device *adev = NULL; + + acpi_get_device_data(handle, &adev, get_acpi_device); + return adev; +} + +void acpi_bus_put_acpi_device(struct acpi_device *adev) +{ + put_device(&adev->dev); +} + int acpi_device_add(struct acpi_device *device, void (*release)(struct device *)) { @@ -1641,6 +1671,27 @@ bool acpi_bay_match(acpi_handle handle) return acpi_ata_match(phandle); } +bool acpi_device_is_battery(struct acpi_device *adev) +{ + struct acpi_hardware_id *hwid; + + list_for_each_entry(hwid, &adev->pnp.ids, list) + if (!strcmp("PNP0C0A", hwid->id)) + return true; + + return false; +} + +static bool is_ejectable_bay(struct acpi_device *adev) +{ + acpi_handle handle = adev->handle; + + if (acpi_has_method(handle, "_EJ0") && acpi_device_is_battery(adev)) + return true; + + return acpi_bay_match(handle); +} + /* * acpi_dock_match - see if an acpi object has a _DCK method */ @@ -1706,6 +1757,20 @@ static bool acpi_ibm_smbus_match(acpi_handle handle) return false; } +static bool acpi_object_is_system_bus(acpi_handle handle) +{ + acpi_handle tmp; + + if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_SB", &tmp)) && + tmp == handle) + return true; + if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_TZ", &tmp)) && + tmp == handle) + return true; + + return false; +} + static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, int device_type) { @@ -1757,8 +1822,10 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, acpi_add_id(pnp, ACPI_DOCK_HID); else if (acpi_ibm_smbus_match(handle)) acpi_add_id(pnp, ACPI_SMBUS_IBM_HID); - else if (list_empty(&pnp->ids) && handle == ACPI_ROOT_OBJECT) { - acpi_add_id(pnp, ACPI_BUS_HID); /* \_SB, LNXSYBUS */ + else if (list_empty(&pnp->ids) && + acpi_object_is_system_bus(handle)) { + /* \_SB, \_TZ, LNXSYBUS */ + acpi_add_id(pnp, ACPI_BUS_HID); strcpy(pnp->device_name, ACPI_BUS_DEVICE_NAME); strcpy(pnp->device_class, ACPI_BUS_CLASS); } @@ -1941,33 +2008,23 @@ void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val) mutex_unlock(&acpi_scan_lock); } -static void acpi_scan_init_hotplug(acpi_handle handle, int type) +static void acpi_scan_init_hotplug(struct acpi_device *adev) { - struct acpi_device_pnp pnp = {}; struct acpi_hardware_id *hwid; - struct acpi_scan_handler *handler; - INIT_LIST_HEAD(&pnp.ids); - acpi_set_pnp_ids(handle, &pnp, type); - - if (!pnp.type.hardware_id) - goto out; + if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev)) { + acpi_dock_add(adev); + return; + } + list_for_each_entry(hwid, &adev->pnp.ids, list) { + struct acpi_scan_handler *handler; - /* - * This relies on the fact that acpi_install_notify_handler() will not - * install the same notify handler routine twice for the same handle. - */ - list_for_each_entry(hwid, &pnp.ids, list) { handler = acpi_scan_match_handler(hwid->id, NULL); if (handler) { - acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - acpi_hotplug_notify_cb, handler); + adev->flags.hotplug_notify = true; break; } } - -out: - acpi_free_pnp_ids(&pnp); } static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, @@ -1991,12 +2048,12 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_OK; } - acpi_scan_init_hotplug(handle, type); - acpi_add_single_object(&device, handle, type, sta); if (!device) return AE_CTRL_DEPTH; + acpi_scan_init_hotplug(device); + out: if (!*return_value) *return_value = device; @@ -2015,13 +2072,14 @@ static int acpi_scan_attach_handler(struct acpi_device *device) handler = acpi_scan_match_handler(hwid->id, &devid); if (handler) { + device->handler = handler; ret = handler->attach(device, devid); - if (ret > 0) { - device->handler = handler; + if (ret > 0) break; - } else if (ret < 0) { + + device->handler = NULL; + if (ret < 0) break; - } } } return ret; @@ -2030,8 +2088,12 @@ static int acpi_scan_attach_handler(struct acpi_device *device) static void acpi_bus_attach(struct acpi_device *device) { struct acpi_device *child; + acpi_handle ejd; int ret; + if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd))) + register_dock_dependent_device(device, ejd); + acpi_bus_get_status(device); /* Skip devices that are not present. */ if (!acpi_device_is_present(device)) { @@ -2184,7 +2246,6 @@ int __init acpi_scan_init(void) acpi_cmos_rtc_init(); acpi_container_init(); acpi_memory_hotplug_init(); - acpi_dock_init(); mutex_lock(&acpi_scan_lock); /* diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index b718806657cd..c40fb2e81bbc 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -71,6 +71,17 @@ static int acpi_sleep_prepare(u32 acpi_state) return 0; } +static bool acpi_sleep_state_supported(u8 sleep_state) +{ + acpi_status status; + u8 type_a, type_b; + + status = acpi_get_sleep_type_data(sleep_state, &type_a, &type_b); + return ACPI_SUCCESS(status) && (!acpi_gbl_reduced_hardware + || (acpi_gbl_FADT.sleep_control.address + && acpi_gbl_FADT.sleep_status.address)); +} + #ifdef CONFIG_ACPI_SLEEP static u32 acpi_target_sleep_state = ACPI_STATE_S0; @@ -604,15 +615,9 @@ static void acpi_sleep_suspend_setup(void) { int i; - for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) { - acpi_status status; - u8 type_a, type_b; - - status = acpi_get_sleep_type_data(i, &type_a, &type_b); - if (ACPI_SUCCESS(status)) { + for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) + if (acpi_sleep_state_supported(i)) sleep_states[i] = 1; - } - } suspend_set_ops(old_suspend_ordering ? &acpi_suspend_ops_old : &acpi_suspend_ops); @@ -740,11 +745,7 @@ static const struct platform_hibernation_ops acpi_hibernation_ops_old = { static void acpi_sleep_hibernate_setup(void) { - acpi_status status; - u8 type_a, type_b; - - status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b); - if (ACPI_FAILURE(status)) + if (!acpi_sleep_state_supported(ACPI_STATE_S4)) return; hibernation_set_ops(old_suspend_ordering ? @@ -793,8 +794,6 @@ static void acpi_power_off(void) int __init acpi_sleep_init(void) { - acpi_status status; - u8 type_a, type_b; char supported[ACPI_S_STATE_COUNT * 3 + 1]; char *pos = supported; int i; @@ -806,8 +805,7 @@ int __init acpi_sleep_init(void) acpi_sleep_suspend_setup(); acpi_sleep_hibernate_setup(); - status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); - if (ACPI_SUCCESS(status)) { + if (acpi_sleep_state_supported(ACPI_STATE_S5)) { sleep_states[ACPI_STATE_S5] = 1; pm_power_off_prepare = acpi_power_off_prepare; pm_power_off = acpi_power_off; diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 91a32cefb11f..38cb9782d4b8 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -12,8 +12,6 @@ #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("sysfs"); -#define PREFIX "ACPI: " - #ifdef CONFIG_ACPI_DEBUG /* * ACPI debug sysfs I/F, including: diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 5837f857ac2e..21782290df41 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -23,6 +23,8 @@ * */ +#define pr_fmt(fmt) "ACPI: " fmt + #include <linux/init.h> #include <linux/kernel.h> #include <linux/smp.h> @@ -33,8 +35,6 @@ #include <linux/acpi.h> #include <linux/bootmem.h> -#define PREFIX "ACPI: " - #define ACPI_MAX_TABLES 128 static char *mps_inti_flags_polarity[] = { "dfl", "high", "res", "low" }; @@ -55,10 +55,9 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { struct acpi_madt_local_apic *p = (struct acpi_madt_local_apic *)header; - printk(KERN_INFO PREFIX - "LAPIC (acpi_id[0x%02x] lapic_id[0x%02x] %s)\n", - p->processor_id, p->id, - (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); + pr_info("LAPIC (acpi_id[0x%02x] lapic_id[0x%02x] %s)\n", + p->processor_id, p->id, + (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); } break; @@ -66,11 +65,9 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { struct acpi_madt_local_x2apic *p = (struct acpi_madt_local_x2apic *)header; - printk(KERN_INFO PREFIX - "X2APIC (apic_id[0x%02x] uid[0x%02x] %s)\n", - p->local_apic_id, p->uid, - (p->lapic_flags & ACPI_MADT_ENABLED) ? - "enabled" : "disabled"); + pr_info("X2APIC (apic_id[0x%02x] uid[0x%02x] %s)\n", + p->local_apic_id, p->uid, + (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); } break; @@ -78,9 +75,8 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { struct acpi_madt_io_apic *p = (struct acpi_madt_io_apic *)header; - printk(KERN_INFO PREFIX - "IOAPIC (id[0x%02x] address[0x%08x] gsi_base[%d])\n", - p->id, p->address, p->global_irq_base); + pr_info("IOAPIC (id[0x%02x] address[0x%08x] gsi_base[%d])\n", + p->id, p->address, p->global_irq_base); } break; @@ -88,18 +84,15 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { struct acpi_madt_interrupt_override *p = (struct acpi_madt_interrupt_override *)header; - printk(KERN_INFO PREFIX - "INT_SRC_OVR (bus %d bus_irq %d global_irq %d %s %s)\n", - p->bus, p->source_irq, p->global_irq, - mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK], - mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2]); + pr_info("INT_SRC_OVR (bus %d bus_irq %d global_irq %d %s %s)\n", + p->bus, p->source_irq, p->global_irq, + mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK], + mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2]); if (p->inti_flags & ~(ACPI_MADT_POLARITY_MASK | ACPI_MADT_TRIGGER_MASK)) - printk(KERN_INFO PREFIX - "INT_SRC_OVR unexpected reserved flags: 0x%x\n", - p->inti_flags & + pr_info("INT_SRC_OVR unexpected reserved flags: 0x%x\n", + p->inti_flags & ~(ACPI_MADT_POLARITY_MASK | ACPI_MADT_TRIGGER_MASK)); - } break; @@ -107,11 +100,10 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { struct acpi_madt_nmi_source *p = (struct acpi_madt_nmi_source *)header; - printk(KERN_INFO PREFIX - "NMI_SRC (%s %s global_irq %d)\n", - mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK], - mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2], - p->global_irq); + pr_info("NMI_SRC (%s %s global_irq %d)\n", + mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK], + mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2], + p->global_irq); } break; @@ -119,12 +111,11 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { struct acpi_madt_local_apic_nmi *p = (struct acpi_madt_local_apic_nmi *)header; - printk(KERN_INFO PREFIX - "LAPIC_NMI (acpi_id[0x%02x] %s %s lint[0x%x])\n", - p->processor_id, - mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK ], - mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2], - p->lint); + pr_info("LAPIC_NMI (acpi_id[0x%02x] %s %s lint[0x%x])\n", + p->processor_id, + mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK ], + mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2], + p->lint); } break; @@ -137,12 +128,11 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) polarity = p->inti_flags & ACPI_MADT_POLARITY_MASK; trigger = (p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2; - printk(KERN_INFO PREFIX - "X2APIC_NMI (uid[0x%02x] %s %s lint[0x%x])\n", - p->uid, - mps_inti_flags_polarity[polarity], - mps_inti_flags_trigger[trigger], - p->lint); + pr_info("X2APIC_NMI (uid[0x%02x] %s %s lint[0x%x])\n", + p->uid, + mps_inti_flags_polarity[polarity], + mps_inti_flags_trigger[trigger], + p->lint); } break; @@ -150,9 +140,8 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { struct acpi_madt_local_apic_override *p = (struct acpi_madt_local_apic_override *)header; - printk(KERN_INFO PREFIX - "LAPIC_ADDR_OVR (address[%p])\n", - (void *)(unsigned long)p->address); + pr_info("LAPIC_ADDR_OVR (address[%p])\n", + (void *)(unsigned long)p->address); } break; @@ -160,10 +149,9 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { struct acpi_madt_io_sapic *p = (struct acpi_madt_io_sapic *)header; - printk(KERN_INFO PREFIX - "IOSAPIC (id[0x%x] address[%p] gsi_base[%d])\n", - p->id, (void *)(unsigned long)p->address, - p->global_irq_base); + pr_info("IOSAPIC (id[0x%x] address[%p] gsi_base[%d])\n", + p->id, (void *)(unsigned long)p->address, + p->global_irq_base); } break; @@ -171,10 +159,9 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { struct acpi_madt_local_sapic *p = (struct acpi_madt_local_sapic *)header; - printk(KERN_INFO PREFIX - "LSAPIC (acpi_id[0x%02x] lsapic_id[0x%02x] lsapic_eid[0x%02x] %s)\n", - p->processor_id, p->id, p->eid, - (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); + pr_info("LSAPIC (acpi_id[0x%02x] lsapic_id[0x%02x] lsapic_eid[0x%02x] %s)\n", + p->processor_id, p->id, p->eid, + (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); } break; @@ -182,19 +169,17 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { struct acpi_madt_interrupt_source *p = (struct acpi_madt_interrupt_source *)header; - printk(KERN_INFO PREFIX - "PLAT_INT_SRC (%s %s type[0x%x] id[0x%04x] eid[0x%x] iosapic_vector[0x%x] global_irq[0x%x]\n", - mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK], - mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2], - p->type, p->id, p->eid, p->io_sapic_vector, - p->global_irq); + pr_info("PLAT_INT_SRC (%s %s type[0x%x] id[0x%04x] eid[0x%x] iosapic_vector[0x%x] global_irq[0x%x]\n", + mps_inti_flags_polarity[p->inti_flags & ACPI_MADT_POLARITY_MASK], + mps_inti_flags_trigger[(p->inti_flags & ACPI_MADT_TRIGGER_MASK) >> 2], + p->type, p->id, p->eid, p->io_sapic_vector, + p->global_irq); } break; default: - printk(KERN_WARNING PREFIX - "Found unsupported MADT entry (type = 0x%x)\n", - header->type); + pr_warn("Found unsupported MADT entry (type = 0x%x)\n", + header->type); break; } } @@ -225,7 +210,7 @@ acpi_table_parse_entries(char *id, acpi_get_table_with_size(id, 0, &table_header, &tbl_size); if (!table_header) { - printk(KERN_WARNING PREFIX "%4.4s not present\n", id); + pr_warn("%4.4s not present\n", id); return -ENODEV; } @@ -248,7 +233,7 @@ acpi_table_parse_entries(char *id, * infinite loop. */ if (entry->length == 0) { - pr_err(PREFIX "[%4.4s:0x%02x] Invalid zero length\n", id, entry_id); + pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, entry_id); goto err; } @@ -256,8 +241,8 @@ acpi_table_parse_entries(char *id, ((unsigned long)entry + entry->length); } if (max_entries && count > max_entries) { - printk(KERN_WARNING PREFIX "[%4.4s:0x%02x] ignored %i entries of " - "%i found\n", id, entry_id, count - max_entries, count); + pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", + id, entry_id, count - max_entries, count); } early_acpi_os_unmap_memory((char *)table_header, tbl_size); @@ -322,13 +307,11 @@ static void __init check_multiple_madt(void) acpi_get_table_with_size(ACPI_SIG_MADT, 2, &table, &tbl_size); if (table) { - printk(KERN_WARNING PREFIX - "BIOS bug: multiple APIC/MADT found," - " using %d\n", acpi_apic_instance); - printk(KERN_WARNING PREFIX - "If \"acpi_apic_instance=%d\" works better, " - "notify linux-acpi@vger.kernel.org\n", - acpi_apic_instance ? 0 : 2); + pr_warn("BIOS bug: multiple APIC/MADT found, using %d\n", + acpi_apic_instance); + pr_warn("If \"acpi_apic_instance=%d\" works better, " + "notify linux-acpi@vger.kernel.org\n", + acpi_apic_instance ? 0 : 2); early_acpi_os_unmap_memory(table, tbl_size); } else @@ -365,8 +348,7 @@ static int __init acpi_parse_apic_instance(char *str) acpi_apic_instance = simple_strtoul(str, NULL, 0); - printk(KERN_NOTICE PREFIX "Shall use APIC/MADT table %d\n", - acpi_apic_instance); + pr_notice("Shall use APIC/MADT table %d\n", acpi_apic_instance); return 0; } diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 08626c851be7..964068553334 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -43,6 +43,7 @@ #include <linux/device.h> #include <linux/thermal.h> #include <linux/acpi.h> +#include <linux/workqueue.h> #include <asm/uaccess.h> #define PREFIX "ACPI: " @@ -90,6 +91,8 @@ static int psv; module_param(psv, int, 0644); MODULE_PARM_DESC(psv, "Disable or override all passive trip points."); +static struct workqueue_struct *acpi_thermal_pm_queue; + static int acpi_thermal_add(struct acpi_device *device); static int acpi_thermal_remove(struct acpi_device *device); static void acpi_thermal_notify(struct acpi_device *device, u32 event); @@ -101,11 +104,13 @@ static const struct acpi_device_id thermal_device_ids[] = { MODULE_DEVICE_TABLE(acpi, thermal_device_ids); #ifdef CONFIG_PM_SLEEP +static int acpi_thermal_suspend(struct device *dev); static int acpi_thermal_resume(struct device *dev); #else +#define acpi_thermal_suspend NULL #define acpi_thermal_resume NULL #endif -static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, NULL, acpi_thermal_resume); +static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume); static struct acpi_driver acpi_thermal_driver = { .name = "thermal", @@ -186,6 +191,7 @@ struct acpi_thermal { struct thermal_zone_device *thermal_zone; int tz_enabled; int kelvin_offset; + struct work_struct thermal_check_work; }; /* -------------------------------------------------------------------------- @@ -1064,6 +1070,13 @@ static void acpi_thermal_guess_offset(struct acpi_thermal *tz) tz->kelvin_offset = 2732; } +static void acpi_thermal_check_fn(struct work_struct *work) +{ + struct acpi_thermal *tz = container_of(work, struct acpi_thermal, + thermal_check_work); + acpi_thermal_check(tz); +} + static int acpi_thermal_add(struct acpi_device *device) { int result = 0; @@ -1093,6 +1106,8 @@ static int acpi_thermal_add(struct acpi_device *device) if (result) goto free_memory; + INIT_WORK(&tz->thermal_check_work, acpi_thermal_check_fn); + pr_info(PREFIX "%s [%s] (%ld C)\n", acpi_device_name(device), acpi_device_bid(device), KELVIN_TO_CELSIUS(tz->temperature)); goto end; @@ -1110,6 +1125,7 @@ static int acpi_thermal_remove(struct acpi_device *device) if (!device || !acpi_driver_data(device)) return -EINVAL; + flush_workqueue(acpi_thermal_pm_queue); tz = acpi_driver_data(device); acpi_thermal_unregister_thermal_zone(tz); @@ -1118,6 +1134,13 @@ static int acpi_thermal_remove(struct acpi_device *device) } #ifdef CONFIG_PM_SLEEP +static int acpi_thermal_suspend(struct device *dev) +{ + /* Make sure the previously queued thermal check work has been done */ + flush_workqueue(acpi_thermal_pm_queue); + return 0; +} + static int acpi_thermal_resume(struct device *dev) { struct acpi_thermal *tz; @@ -1148,7 +1171,7 @@ static int acpi_thermal_resume(struct device *dev) tz->state.active |= tz->trips.active[i].flags.enabled; } - acpi_thermal_check(tz); + queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work); return AE_OK; } @@ -1240,16 +1263,22 @@ static int __init acpi_thermal_init(void) return -ENODEV; } + acpi_thermal_pm_queue = create_workqueue("acpi_thermal_pm"); + if (!acpi_thermal_pm_queue) + return -ENODEV; + result = acpi_bus_register_driver(&acpi_thermal_driver); - if (result < 0) + if (result < 0) { + destroy_workqueue(acpi_thermal_pm_queue); return -ENODEV; + } return 0; } static void __exit acpi_thermal_exit(void) { - + destroy_workqueue(acpi_thermal_pm_queue); acpi_bus_unregister_driver(&acpi_thermal_driver); return; diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 85e3b612bdc0..0f5f78fa6545 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -422,7 +422,7 @@ out: EXPORT_SYMBOL(acpi_get_physical_device_location); /** - * acpi_evaluate_hotplug_ost: Evaluate _OST for hotplug operations + * acpi_evaluate_ost: Evaluate _OST for hotplug operations * @handle: ACPI device handle * @source_event: source event code * @status_code: status code @@ -433,17 +433,15 @@ EXPORT_SYMBOL(acpi_get_physical_device_location); * When the platform does not support _OST, this function has no effect. */ acpi_status -acpi_evaluate_hotplug_ost(acpi_handle handle, u32 source_event, - u32 status_code, struct acpi_buffer *status_buf) +acpi_evaluate_ost(acpi_handle handle, u32 source_event, u32 status_code, + struct acpi_buffer *status_buf) { -#ifdef ACPI_HOTPLUG_OST union acpi_object params[3] = { {.type = ACPI_TYPE_INTEGER,}, {.type = ACPI_TYPE_INTEGER,}, {.type = ACPI_TYPE_BUFFER,} }; struct acpi_object_list arg_list = {3, params}; - acpi_status status; params[0].integer.value = source_event; params[1].integer.value = status_code; @@ -455,13 +453,9 @@ acpi_evaluate_hotplug_ost(acpi_handle handle, u32 source_event, params[2].buffer.length = 0; } - status = acpi_evaluate_object(handle, "_OST", &arg_list, NULL); - return status; -#else - return AE_OK; -#endif + return acpi_evaluate_object(handle, "_OST", &arg_list, NULL); } -EXPORT_SYMBOL(acpi_evaluate_hotplug_ost); +EXPORT_SYMBOL(acpi_evaluate_ost); /** * acpi_handle_printk: Print message with ACPI prefix and object path diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index b6ba88ed31ae..48c7e8af9c96 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -45,8 +45,6 @@ #include "internal.h" -#define PREFIX "ACPI: " - #define ACPI_VIDEO_BUS_NAME "Video Bus" #define ACPI_VIDEO_DEVICE_NAME "Video Device" #define ACPI_VIDEO_NOTIFY_SWITCH 0x80 diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 19080c8e2f2a..33e3db548a29 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -40,8 +40,6 @@ #include "internal.h" -#define PREFIX "ACPI: " - ACPI_MODULE_NAME("video"); #define _COMPONENT ACPI_VIDEO_COMPONENT diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 868429a47be4..20e03a7eb8b4 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -11,13 +11,13 @@ config HAVE_PATA_PLATFORM to update the PATA_PLATFORM entry. menuconfig ATA - tristate "Serial ATA and Parallel ATA drivers" + tristate "Serial ATA and Parallel ATA drivers (libata)" depends on HAS_IOMEM depends on BLOCK depends on !(M32R || M68K || S390) || BROKEN select SCSI ---help--- - If you want to use a ATA hard disk, ATA tape drive, ATA CD-ROM or + If you want to use an ATA hard disk, ATA tape drive, ATA CD-ROM or any other ATA device under Linux, say Y and make sure that you know the name of your ATA host adapter (the card inside your computer that "speaks" the ATA protocol, also called ATA controller), @@ -60,7 +60,7 @@ config ATA_ACPI config SATA_ZPODD bool "SATA Zero Power Optical Disc Drive (ZPODD) support" - depends on ATA_ACPI + depends on ATA_ACPI && PM_RUNTIME default n help This option adds support for SATA Zero Power Optical Disc @@ -97,15 +97,48 @@ config SATA_AHCI_PLATFORM If unsure, say N. +config AHCI_DA850 + tristate "DaVinci DA850 AHCI SATA support" + depends on ARCH_DAVINCI_DA850 + help + This option enables support for the DaVinci DA850 SoC's + onboard AHCI SATA. + + If unsure, say N. + +config AHCI_ST + tristate "ST AHCI SATA support" + depends on ARCH_STI + help + This option enables support for ST AHCI SATA controller. + + If unsure, say N. + config AHCI_IMX tristate "Freescale i.MX AHCI SATA support" - depends on SATA_AHCI_PLATFORM && MFD_SYSCON + depends on MFD_SYSCON help This option enables support for the Freescale i.MX SoC's onboard AHCI SATA. If unsure, say N. +config AHCI_SUNXI + tristate "Allwinner sunxi AHCI SATA support" + depends on ARCH_SUNXI + help + This option enables support for the Allwinner sunxi SoC's + onboard AHCI SATA. + + If unsure, say N. + +config AHCI_XGENE + tristate "APM X-Gene 6.0Gbps AHCI SATA host controller support" + depends on ARM64 || COMPILE_TEST + select PHY_XGENE + help + This option enables support for APM X-Gene SoC SATA host controller. + config SATA_FSL tristate "Freescale 3.0Gbps SATA support" depends on FSL_SOC @@ -239,6 +272,7 @@ config SATA_DWC_VDEBUG config SATA_HIGHBANK tristate "Calxeda Highbank SATA support" + depends on ARCH_HIGHBANK || COMPILE_TEST help This option enables support for the Calxeda Highbank SoC's onboard SATA. @@ -247,6 +281,8 @@ config SATA_HIGHBANK config SATA_MV tristate "Marvell SATA support" + depends on PCI || ARCH_DOVE || ARCH_KIRKWOOD || ARCH_MV78XX0 || \ + ARCH_MVEBU || ARCH_ORION5X || COMPILE_TEST select GENERIC_PHY help This option enables support for the Marvell Serial ATA family. @@ -273,6 +309,7 @@ config SATA_PROMISE config SATA_RCAR tristate "Renesas R-Car SATA support" + depends on ARCH_SHMOBILE || COMPILE_TEST help This option enables support for Renesas R-Car Serial ATA. @@ -352,6 +389,7 @@ config PATA_AMD config PATA_ARASAN_CF tristate "ARASAN CompactFlash PATA Controller Support" + depends on ARCH_SPEAR13XX || COMPILE_TEST depends on DMADEVICES select DMA_ENGINE help @@ -403,7 +441,7 @@ config PATA_CMD64X config PATA_CS5520 tristate "CS5510/5520 PATA support" - depends on PCI + depends on PCI && (X86_32 || COMPILE_TEST) help This option enables support for the Cyrix 5510/5520 companion chip used with the MediaGX/Geode processor family. @@ -412,7 +450,7 @@ config PATA_CS5520 config PATA_CS5530 tristate "CS5530 PATA support" - depends on PCI + depends on PCI && (X86_32 || COMPILE_TEST) help This option enables support for the Cyrix/NatSemi/AMD CS5530 companion chip used with the MediaGX/Geode processor family. @@ -421,7 +459,7 @@ config PATA_CS5530 config PATA_CS5535 tristate "CS5535 PATA support (Experimental)" - depends on PCI && X86 && !X86_64 + depends on PCI && X86_32 help This option enables support for the NatSemi/AMD CS5535 companion chip used with the Geode processor family. @@ -430,7 +468,7 @@ config PATA_CS5535 config PATA_CS5536 tristate "CS5536 PATA support" - depends on PCI + depends on PCI && (X86_32 || MIPS || COMPILE_TEST) help This option enables support for the AMD CS5536 companion chip used with the Geode LX processor family. @@ -666,7 +704,7 @@ config PATA_RDC config PATA_SC1200 tristate "SC1200 PATA support" - depends on PCI + depends on PCI && (X86_32 || COMPILE_TEST) help This option enables support for the NatSemi/AMD SC1200 SoC companion chip used with the Geode processor family. diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 46518c622460..44c8016e565c 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -4,13 +4,17 @@ obj-$(CONFIG_ATA) += libata.o # non-SFF interface obj-$(CONFIG_SATA_AHCI) += ahci.o libahci.o obj-$(CONFIG_SATA_ACARD_AHCI) += acard-ahci.o libahci.o -obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o +obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o libahci_platform.o obj-$(CONFIG_SATA_FSL) += sata_fsl.o obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o obj-$(CONFIG_SATA_SIL24) += sata_sil24.o obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o -obj-$(CONFIG_AHCI_IMX) += ahci_imx.o +obj-$(CONFIG_AHCI_DA850) += ahci_da850.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_IMX) += ahci_imx.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_ST) += ahci_st.o libahci.o libahci_platform.o +obj-$(CONFIG_AHCI_XGENE) += ahci_xgene.o libahci.o libahci_platform.o # SFF w/ custom DMA obj-$(CONFIG_PDC_ADMA) += pdc_adma.o diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c index fd665d919df2..b51605ac5974 100644 --- a/drivers/ata/acard-ahci.c +++ b/drivers/ata/acard-ahci.c @@ -36,7 +36,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index c81d809c111b..5a0bf8ed649b 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -35,7 +35,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -578,6 +577,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; bool online; int rc; @@ -588,7 +588,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), deadline, &online, NULL); - ahci_start_engine(ap); + hpriv->start_engine(ap); DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); @@ -603,6 +603,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, { struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; bool online; @@ -618,7 +619,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), deadline, &online, NULL); - ahci_start_engine(ap); + hpriv->start_engine(ap); /* The pseudo configuration device on SIMG4726 attached to * ASUS P5W-DH Deluxe doesn't send signature FIS after @@ -1165,13 +1166,13 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host) static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, struct ahci_host_priv *hpriv) { - int rc, nvec; + int nvec; if (hpriv->flags & AHCI_HFLAG_NO_MSI) goto intx; - rc = pci_msi_vec_count(pdev); - if (rc < 0) + nvec = pci_msi_vec_count(pdev); + if (nvec < 0) goto intx; /* @@ -1179,21 +1180,19 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, * Message mode could be enforced. In this case assume that advantage * of multipe MSIs is negated and use single MSI mode instead. */ - if (rc < n_ports) + if (nvec < n_ports) goto single_msi; - nvec = rc; - rc = pci_enable_msi_block(pdev, nvec); - if (rc < 0) - goto intx; - else if (rc > 0) + nvec = pci_enable_msi_range(pdev, nvec, nvec); + if (nvec == -ENOSPC) goto single_msi; + else if (nvec < 0) + goto intx; return nvec; single_msi: - rc = pci_enable_msi(pdev); - if (rc) + if (pci_enable_msi(pdev)) goto intx; return 1; diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 2289efdf8203..51af275b3388 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -37,6 +37,8 @@ #include <linux/clk.h> #include <linux/libata.h> +#include <linux/phy/phy.h> +#include <linux/regulator/consumer.h> /* Enclosure Management Control */ #define EM_CTRL_MSG_TYPE 0x000f0000 @@ -51,6 +53,7 @@ enum { AHCI_MAX_PORTS = 32, + AHCI_MAX_CLKS = 3, AHCI_MAX_SG = 168, /* hardware max is 64K */ AHCI_DMA_BOUNDARY = 0xffffffff, AHCI_MAX_CMDS = 32, @@ -321,8 +324,17 @@ struct ahci_host_priv { u32 em_loc; /* enclosure management location */ u32 em_buf_sz; /* EM buffer size in byte */ u32 em_msg_type; /* EM message type */ - struct clk *clk; /* Only for platforms supporting clk */ + bool got_runtime_pm; /* Did we do pm_runtime_get? */ + struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ + struct regulator *target_pwr; /* Optional */ + struct phy *phy; /* If platform uses phy */ void *plat_data; /* Other platform data */ + /* + * Optional ahci_start_engine override, if not set this gets set to the + * default ahci_start_engine during ahci_save_initial_config, this can + * be overridden anytime before the host is activated. + */ + void (*start_engine)(struct ata_port *ap); }; extern int ahci_ignore_sss; diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c new file mode 100644 index 000000000000..2c83613ce2db --- /dev/null +++ b/drivers/ata/ahci_da850.c @@ -0,0 +1,114 @@ +/* + * DaVinci DA850 AHCI SATA platform 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, or (at your option) + * any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/libata.h> +#include <linux/ahci_platform.h> +#include "ahci.h" + +/* SATA PHY Control Register offset from AHCI base */ +#define SATA_P0PHYCR_REG 0x178 + +#define SATA_PHY_MPY(x) ((x) << 0) +#define SATA_PHY_LOS(x) ((x) << 6) +#define SATA_PHY_RXCDR(x) ((x) << 10) +#define SATA_PHY_RXEQ(x) ((x) << 13) +#define SATA_PHY_TXSWING(x) ((x) << 19) +#define SATA_PHY_ENPLL(x) ((x) << 31) + +/* + * The multiplier needed for 1.5GHz PLL output. + * + * NOTE: This is currently hardcoded to be suitable for 100MHz crystal + * frequency (which is used by DA850 EVM board) and may need to be changed + * if you would like to use this driver on some other board. + */ +#define DA850_SATA_CLK_MULTIPLIER 7 + +static void da850_sata_init(struct device *dev, void __iomem *pwrdn_reg, + void __iomem *ahci_base) +{ + unsigned int val; + + /* Enable SATA clock receiver */ + val = readl(pwrdn_reg); + val &= ~BIT(0); + writel(val, pwrdn_reg); + + val = SATA_PHY_MPY(DA850_SATA_CLK_MULTIPLIER + 1) | SATA_PHY_LOS(1) | + SATA_PHY_RXCDR(4) | SATA_PHY_RXEQ(1) | SATA_PHY_TXSWING(3) | + SATA_PHY_ENPLL(1); + + writel(val, ahci_base + SATA_P0PHYCR_REG); +} + +static const struct ata_port_info ahci_da850_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_ops, +}; + +static int ahci_da850_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + struct resource *res; + void __iomem *pwrdn_reg; + int rc; + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + goto disable_resources; + + pwrdn_reg = devm_ioremap(dev, res->start, resource_size(res)); + if (!pwrdn_reg) + goto disable_resources; + + da850_sata_init(dev, pwrdn_reg, hpriv->mmio); + + rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info, 0, 0); + if (rc) + goto disable_resources; + + return 0; +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} + +static SIMPLE_DEV_PM_OPS(ahci_da850_pm_ops, ahci_platform_suspend, + ahci_platform_resume); + +static struct platform_driver ahci_da850_driver = { + .probe = ahci_da850_probe, + .remove = ata_platform_remove_one, + .driver = { + .name = "ahci_da850", + .owner = THIS_MODULE, + .pm = &ahci_da850_pm_ops, + }, +}; +module_platform_driver(ahci_da850_driver); + +MODULE_DESCRIPTION("DaVinci DA850 AHCI SATA platform driver"); +MODULE_AUTHOR("Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index dd4d6f74d7bd..497c7abe1c7d 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -42,13 +42,7 @@ enum ahci_imx_type { struct imx_ahci_priv { struct platform_device *ahci_pdev; enum ahci_imx_type type; - - /* i.MX53 clock */ - struct clk *sata_gate_clk; - /* Common clock */ - struct clk *sata_ref_clk; struct clk *ahb_clk; - struct regmap *gpr; bool no_device; bool first_time; @@ -58,28 +52,52 @@ static int ahci_imx_hotplug; module_param_named(hotplug, ahci_imx_hotplug, int, 0644); MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support)"); -static int imx_sata_clock_enable(struct device *dev) +static void ahci_imx_host_stop(struct ata_host *host); + +static int imx_sata_enable(struct ahci_host_priv *hpriv) { - struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); + struct imx_ahci_priv *imxpriv = hpriv->plat_data; int ret; - if (imxpriv->type == AHCI_IMX53) { - ret = clk_prepare_enable(imxpriv->sata_gate_clk); - if (ret < 0) { - dev_err(dev, "prepare-enable sata_gate clock err:%d\n", - ret); + if (imxpriv->no_device) + return 0; + + if (hpriv->target_pwr) { + ret = regulator_enable(hpriv->target_pwr); + if (ret) return ret; - } } - ret = clk_prepare_enable(imxpriv->sata_ref_clk); - if (ret < 0) { - dev_err(dev, "prepare-enable sata_ref clock err:%d\n", - ret); - goto clk_err; - } + ret = ahci_platform_enable_clks(hpriv); + if (ret < 0) + goto disable_regulator; if (imxpriv->type == AHCI_IMX6Q) { + /* + * set PHY Paremeters, two steps to configure the GPR13, + * one write for rest of parameters, mask of first write + * is 0x07ffffff, and the other one write for setting + * the mpll_clk_en. + */ + regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, + IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK | + IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK | + IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK | + IMX6Q_GPR13_SATA_SPD_MODE_MASK | + IMX6Q_GPR13_SATA_MPLL_SS_EN | + IMX6Q_GPR13_SATA_TX_ATTEN_MASK | + IMX6Q_GPR13_SATA_TX_BOOST_MASK | + IMX6Q_GPR13_SATA_TX_LVL_MASK | + IMX6Q_GPR13_SATA_MPLL_CLK_EN | + IMX6Q_GPR13_SATA_TX_EDGE_RATE, + IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB | + IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M | + IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F | + IMX6Q_GPR13_SATA_SPD_MODE_3P0G | + IMX6Q_GPR13_SATA_MPLL_SS_EN | + IMX6Q_GPR13_SATA_TX_ATTEN_9_16 | + IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB | + IMX6Q_GPR13_SATA_TX_LVL_1_025_V); regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, IMX6Q_GPR13_SATA_MPLL_CLK_EN, IMX6Q_GPR13_SATA_MPLL_CLK_EN); @@ -89,15 +107,19 @@ static int imx_sata_clock_enable(struct device *dev) return 0; -clk_err: - if (imxpriv->type == AHCI_IMX53) - clk_disable_unprepare(imxpriv->sata_gate_clk); +disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); + return ret; } -static void imx_sata_clock_disable(struct device *dev) +static void imx_sata_disable(struct ahci_host_priv *hpriv) { - struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); + struct imx_ahci_priv *imxpriv = hpriv->plat_data; + + if (imxpriv->no_device) + return; if (imxpriv->type == AHCI_IMX6Q) { regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, @@ -105,10 +127,10 @@ static void imx_sata_clock_disable(struct device *dev) !IMX6Q_GPR13_SATA_MPLL_CLK_EN); } - clk_disable_unprepare(imxpriv->sata_ref_clk); + ahci_platform_disable_clks(hpriv); - if (imxpriv->type == AHCI_IMX53) - clk_disable_unprepare(imxpriv->sata_gate_clk); + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); } static void ahci_imx_error_handler(struct ata_port *ap) @@ -118,7 +140,7 @@ static void ahci_imx_error_handler(struct ata_port *ap) struct ata_host *host = dev_get_drvdata(ap->dev); struct ahci_host_priv *hpriv = host->private_data; void __iomem *mmio = hpriv->mmio; - struct imx_ahci_priv *imxpriv = dev_get_drvdata(ap->dev->parent); + struct imx_ahci_priv *imxpriv = hpriv->plat_data; ahci_error_handler(ap); @@ -136,7 +158,7 @@ static void ahci_imx_error_handler(struct ata_port *ap) */ reg_val = readl(mmio + PORT_PHY_CTL); writel(reg_val | PORT_PHY_CTL_PDDQ_LOC, mmio + PORT_PHY_CTL); - imx_sata_clock_disable(ap->dev); + imx_sata_disable(hpriv); imxpriv->no_device = true; } @@ -144,7 +166,9 @@ static int ahci_imx_softreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { struct ata_port *ap = link->ap; - struct imx_ahci_priv *imxpriv = dev_get_drvdata(ap->dev->parent); + struct ata_host *host = dev_get_drvdata(ap->dev); + struct ahci_host_priv *hpriv = host->private_data; + struct imx_ahci_priv *imxpriv = hpriv->plat_data; int ret = -EIO; if (imxpriv->type == AHCI_IMX53) @@ -156,7 +180,8 @@ static int ahci_imx_softreset(struct ata_link *link, unsigned int *class, } static struct ata_port_operations ahci_imx_ops = { - .inherits = &ahci_platform_ops, + .inherits = &ahci_ops, + .host_stop = ahci_imx_host_stop, .error_handler = ahci_imx_error_handler, .softreset = ahci_imx_softreset, }; @@ -168,79 +193,6 @@ static const struct ata_port_info ahci_imx_port_info = { .port_ops = &ahci_imx_ops, }; -static int imx_sata_init(struct device *dev, void __iomem *mmio) -{ - int ret = 0; - unsigned int reg_val; - struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); - - ret = imx_sata_clock_enable(dev); - if (ret < 0) - return ret; - - /* - * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, - * and IP vendor specific register HOST_TIMER1MS. - * Configure CAP_SSS (support stagered spin up). - * Implement the port0. - * Get the ahb clock rate, and configure the TIMER1MS register. - */ - reg_val = readl(mmio + HOST_CAP); - if (!(reg_val & HOST_CAP_SSS)) { - reg_val |= HOST_CAP_SSS; - writel(reg_val, mmio + HOST_CAP); - } - reg_val = readl(mmio + HOST_PORTS_IMPL); - if (!(reg_val & 0x1)) { - reg_val |= 0x1; - writel(reg_val, mmio + HOST_PORTS_IMPL); - } - - reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; - writel(reg_val, mmio + HOST_TIMER1MS); - - return 0; -} - -static void imx_sata_exit(struct device *dev) -{ - imx_sata_clock_disable(dev); -} - -static int imx_ahci_suspend(struct device *dev) -{ - struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); - - /* - * If no_device is set, The CLKs had been gated off in the - * initialization so don't do it again here. - */ - if (!imxpriv->no_device) - imx_sata_clock_disable(dev); - - return 0; -} - -static int imx_ahci_resume(struct device *dev) -{ - struct imx_ahci_priv *imxpriv = dev_get_drvdata(dev->parent); - int ret = 0; - - if (!imxpriv->no_device) - ret = imx_sata_clock_enable(dev); - - return ret; -} - -static struct ahci_platform_data imx_sata_pdata = { - .init = imx_sata_init, - .exit = imx_sata_exit, - .ata_port_info = &ahci_imx_port_info, - .suspend = imx_ahci_suspend, - .resume = imx_ahci_resume, - -}; - static const struct of_device_id imx_ahci_of_match[] = { { .compatible = "fsl,imx53-ahci", .data = (void *)AHCI_IMX53 }, { .compatible = "fsl,imx6q-ahci", .data = (void *)AHCI_IMX6Q }, @@ -251,151 +203,124 @@ MODULE_DEVICE_TABLE(of, imx_ahci_of_match); static int imx_ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *mem, *irq, res[2]; const struct of_device_id *of_id; - enum ahci_imx_type type; - const struct ahci_platform_data *pdata = NULL; + struct ahci_host_priv *hpriv; struct imx_ahci_priv *imxpriv; - struct device *ahci_dev; - struct platform_device *ahci_pdev; + unsigned int reg_val; int ret; of_id = of_match_device(imx_ahci_of_match, dev); if (!of_id) return -EINVAL; - type = (enum ahci_imx_type)of_id->data; - pdata = &imx_sata_pdata; - imxpriv = devm_kzalloc(dev, sizeof(*imxpriv), GFP_KERNEL); - if (!imxpriv) { - dev_err(dev, "can't alloc ahci_host_priv\n"); + if (!imxpriv) return -ENOMEM; - } - - ahci_pdev = platform_device_alloc("ahci", -1); - if (!ahci_pdev) - return -ENODEV; - - ahci_dev = &ahci_pdev->dev; - ahci_dev->parent = dev; imxpriv->no_device = false; imxpriv->first_time = true; - imxpriv->type = type; - + imxpriv->type = (enum ahci_imx_type)of_id->data; imxpriv->ahb_clk = devm_clk_get(dev, "ahb"); if (IS_ERR(imxpriv->ahb_clk)) { dev_err(dev, "can't get ahb clock.\n"); - ret = PTR_ERR(imxpriv->ahb_clk); - goto err_out; + return PTR_ERR(imxpriv->ahb_clk); } - if (type == AHCI_IMX53) { - imxpriv->sata_gate_clk = devm_clk_get(dev, "sata_gate"); - if (IS_ERR(imxpriv->sata_gate_clk)) { - dev_err(dev, "can't get sata_gate clock.\n"); - ret = PTR_ERR(imxpriv->sata_gate_clk); - goto err_out; + if (imxpriv->type == AHCI_IMX6Q) { + imxpriv->gpr = syscon_regmap_lookup_by_compatible( + "fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(imxpriv->gpr)) { + dev_err(dev, + "failed to find fsl,imx6q-iomux-gpr regmap\n"); + return PTR_ERR(imxpriv->gpr); } } - imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref"); - if (IS_ERR(imxpriv->sata_ref_clk)) { - dev_err(dev, "can't get sata_ref clock.\n"); - ret = PTR_ERR(imxpriv->sata_ref_clk); - goto err_out; - } + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + hpriv->plat_data = imxpriv; - imxpriv->ahci_pdev = ahci_pdev; - platform_set_drvdata(pdev, imxpriv); + ret = imx_sata_enable(hpriv); + if (ret) + return ret; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!mem || !irq) { - dev_err(dev, "no mmio/irq resource\n"); - ret = -ENOMEM; - goto err_out; + /* + * Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL, + * and IP vendor specific register HOST_TIMER1MS. + * Configure CAP_SSS (support stagered spin up). + * Implement the port0. + * Get the ahb clock rate, and configure the TIMER1MS register. + */ + reg_val = readl(hpriv->mmio + HOST_CAP); + if (!(reg_val & HOST_CAP_SSS)) { + reg_val |= HOST_CAP_SSS; + writel(reg_val, hpriv->mmio + HOST_CAP); + } + reg_val = readl(hpriv->mmio + HOST_PORTS_IMPL); + if (!(reg_val & 0x1)) { + reg_val |= 0x1; + writel(reg_val, hpriv->mmio + HOST_PORTS_IMPL); } - res[0] = *mem; - res[1] = *irq; + reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; + writel(reg_val, hpriv->mmio + HOST_TIMER1MS); - ahci_dev->coherent_dma_mask = DMA_BIT_MASK(32); - ahci_dev->dma_mask = &ahci_dev->coherent_dma_mask; - ahci_dev->of_node = dev->of_node; + ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, 0, 0); + if (ret) + imx_sata_disable(hpriv); - if (type == AHCI_IMX6Q) { - imxpriv->gpr = syscon_regmap_lookup_by_compatible( - "fsl,imx6q-iomuxc-gpr"); - if (IS_ERR(imxpriv->gpr)) { - dev_err(dev, - "failed to find fsl,imx6q-iomux-gpr regmap\n"); - ret = PTR_ERR(imxpriv->gpr); - goto err_out; - } + return ret; +} - /* - * Set PHY Paremeters, two steps to configure the GPR13, - * one write for rest of parameters, mask of first write - * is 0x07fffffe, and the other one write for setting - * the mpll_clk_en happens in imx_sata_clock_enable(). - */ - regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13, - IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK | - IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK | - IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK | - IMX6Q_GPR13_SATA_SPD_MODE_MASK | - IMX6Q_GPR13_SATA_MPLL_SS_EN | - IMX6Q_GPR13_SATA_TX_ATTEN_MASK | - IMX6Q_GPR13_SATA_TX_BOOST_MASK | - IMX6Q_GPR13_SATA_TX_LVL_MASK | - IMX6Q_GPR13_SATA_MPLL_CLK_EN | - IMX6Q_GPR13_SATA_TX_EDGE_RATE, - IMX6Q_GPR13_SATA_RX_EQ_VAL_3_0_DB | - IMX6Q_GPR13_SATA_RX_LOS_LVL_SATA2M | - IMX6Q_GPR13_SATA_RX_DPLL_MODE_2P_4F | - IMX6Q_GPR13_SATA_SPD_MODE_3P0G | - IMX6Q_GPR13_SATA_MPLL_SS_EN | - IMX6Q_GPR13_SATA_TX_ATTEN_9_16 | - IMX6Q_GPR13_SATA_TX_BOOST_3_33_DB | - IMX6Q_GPR13_SATA_TX_LVL_1_025_V); - } +static void ahci_imx_host_stop(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; - ret = platform_device_add_resources(ahci_pdev, res, 2); - if (ret) - goto err_out; + imx_sata_disable(hpriv); +} - ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata)); - if (ret) - goto err_out; +#ifdef CONFIG_PM_SLEEP +static int imx_ahci_suspend(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int ret; - ret = platform_device_add(ahci_pdev); - if (ret) { -err_out: - platform_device_put(ahci_pdev); + ret = ahci_platform_suspend_host(dev); + if (ret) return ret; - } + + imx_sata_disable(hpriv); return 0; } -static int imx_ahci_remove(struct platform_device *pdev) +static int imx_ahci_resume(struct device *dev) { - struct imx_ahci_priv *imxpriv = platform_get_drvdata(pdev); - struct platform_device *ahci_pdev = imxpriv->ahci_pdev; + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int ret; - platform_device_unregister(ahci_pdev); - return 0; + ret = imx_sata_enable(hpriv); + if (ret) + return ret; + + return ahci_platform_resume_host(dev); } +#endif + +static SIMPLE_DEV_PM_OPS(ahci_imx_pm_ops, imx_ahci_suspend, imx_ahci_resume); static struct platform_driver imx_ahci_driver = { .probe = imx_ahci_probe, - .remove = imx_ahci_remove, + .remove = ata_platform_remove_one, .driver = { .name = "ahci-imx", .owner = THIS_MODULE, .of_match_table = imx_ahci_of_match, + .pm = &ahci_imx_pm_ops, }, }; module_platform_driver(imx_ahci_driver); diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 4b231baceb09..ef67e79944f9 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -12,135 +12,36 @@ * any later version. */ -#include <linux/clk.h> #include <linux/kernel.h> -#include <linux/gfp.h> #include <linux/module.h> #include <linux/pm.h> -#include <linux/init.h> -#include <linux/interrupt.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/libata.h> #include <linux/ahci_platform.h> #include "ahci.h" -static void ahci_host_stop(struct ata_host *host); - -enum ahci_type { - AHCI, /* standard platform ahci */ - IMX53_AHCI, /* ahci on i.mx53 */ - STRICT_AHCI, /* delayed DMA engine start */ -}; - -static struct platform_device_id ahci_devtype[] = { - { - .name = "ahci", - .driver_data = AHCI, - }, { - .name = "imx53-ahci", - .driver_data = IMX53_AHCI, - }, { - .name = "strict-ahci", - .driver_data = STRICT_AHCI, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, ahci_devtype); - -struct ata_port_operations ahci_platform_ops = { - .inherits = &ahci_ops, - .host_stop = ahci_host_stop, -}; -EXPORT_SYMBOL_GPL(ahci_platform_ops); - -static struct ata_port_operations ahci_platform_retry_srst_ops = { - .inherits = &ahci_pmp_retry_srst_ops, - .host_stop = ahci_host_stop, -}; - -static const struct ata_port_info ahci_port_info[] = { - /* by features */ - [AHCI] = { - .flags = AHCI_FLAG_COMMON, - .pio_mask = ATA_PIO4, - .udma_mask = ATA_UDMA6, - .port_ops = &ahci_platform_ops, - }, - [IMX53_AHCI] = { - .flags = AHCI_FLAG_COMMON, - .pio_mask = ATA_PIO4, - .udma_mask = ATA_UDMA6, - .port_ops = &ahci_platform_retry_srst_ops, - }, - [STRICT_AHCI] = { - AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE), - .flags = AHCI_FLAG_COMMON, - .pio_mask = ATA_PIO4, - .udma_mask = ATA_UDMA6, - .port_ops = &ahci_platform_ops, - }, -}; - -static struct scsi_host_template ahci_platform_sht = { - AHCI_SHT("ahci_platform"), +static const struct ata_port_info ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_ops, }; static int ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ahci_platform_data *pdata = dev_get_platdata(dev); - const struct platform_device_id *id = platform_get_device_id(pdev); - struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0]; - const struct ata_port_info *ppi[] = { &pi, NULL }; struct ahci_host_priv *hpriv; - struct ata_host *host; - struct resource *mem; - int irq; - int n_ports; - int i; int rc; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(dev, "no mmio space\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_err(dev, "no irq\n"); - return -EINVAL; - } - - if (pdata && pdata->ata_port_info) - pi = *pdata->ata_port_info; - - hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) { - dev_err(dev, "can't alloc ahci_host_priv\n"); - return -ENOMEM; - } - - hpriv->flags |= (unsigned long)pi.private_data; - - hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); - if (!hpriv->mmio) { - dev_err(dev, "can't map %pR\n", mem); - return -ENOMEM; - } + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); - hpriv->clk = clk_get(dev, NULL); - if (IS_ERR(hpriv->clk)) { - dev_err(dev, "can't get clock\n"); - } else { - rc = clk_prepare_enable(hpriv->clk); - if (rc) { - dev_err(dev, "clock prepare enable failed"); - goto free_clk; - } - } + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; /* * Some platforms might need to prepare for mmio region access, @@ -151,69 +52,10 @@ static int ahci_probe(struct platform_device *pdev) if (pdata && pdata->init) { rc = pdata->init(dev, hpriv->mmio); if (rc) - goto disable_unprepare_clk; - } - - ahci_save_initial_config(dev, hpriv, - pdata ? pdata->force_port_map : 0, - pdata ? pdata->mask_port_map : 0); - - /* prepare host */ - if (hpriv->cap & HOST_CAP_NCQ) - pi.flags |= ATA_FLAG_NCQ; - - if (hpriv->cap & HOST_CAP_PMP) - pi.flags |= ATA_FLAG_PMP; - - ahci_set_em_messages(hpriv, &pi); - - /* CAP.NP sometimes indicate the index of the last enabled - * port, at other times, that of the last possible port, so - * determining the maximum port number requires looking at - * both CAP.NP and port_map. - */ - n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); - - host = ata_host_alloc_pinfo(dev, ppi, n_ports); - if (!host) { - rc = -ENOMEM; - goto pdata_exit; - } - - host->private_data = hpriv; - - if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) - host->flags |= ATA_HOST_PARALLEL_SCAN; - else - dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); - - if (pi.flags & ATA_FLAG_EM) - ahci_reset_em(host); - - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - - ata_port_desc(ap, "mmio %pR", mem); - ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); - - /* set enclosure management message type */ - if (ap->flags & ATA_FLAG_EM) - ap->em_message_type = hpriv->em_msg_type; - - /* disabled/not-implemented port */ - if (!(hpriv->port_map & (1 << i))) - ap->ops = &ata_dummy_port_ops; + goto disable_resources; } - rc = ahci_reset_controller(host); - if (rc) - goto pdata_exit; - - ahci_init_controller(host); - ahci_print_info(host, "platform"); - - rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, - &ahci_platform_sht); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, 0, 0); if (rc) goto pdata_exit; @@ -221,115 +63,19 @@ static int ahci_probe(struct platform_device *pdev) pdata_exit: if (pdata && pdata->exit) pdata->exit(dev); -disable_unprepare_clk: - if (!IS_ERR(hpriv->clk)) - clk_disable_unprepare(hpriv->clk); -free_clk: - if (!IS_ERR(hpriv->clk)) - clk_put(hpriv->clk); - return rc; -} - -static void ahci_host_stop(struct ata_host *host) -{ - struct device *dev = host->dev; - struct ahci_platform_data *pdata = dev_get_platdata(dev); - struct ahci_host_priv *hpriv = host->private_data; - - if (pdata && pdata->exit) - pdata->exit(dev); - - if (!IS_ERR(hpriv->clk)) { - clk_disable_unprepare(hpriv->clk); - clk_put(hpriv->clk); - } -} - -#ifdef CONFIG_PM_SLEEP -static int ahci_suspend(struct device *dev) -{ - struct ahci_platform_data *pdata = dev_get_platdata(dev); - struct ata_host *host = dev_get_drvdata(dev); - struct ahci_host_priv *hpriv = host->private_data; - void __iomem *mmio = hpriv->mmio; - u32 ctl; - int rc; - - if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { - dev_err(dev, "firmware update required for suspend/resume\n"); - return -EIO; - } - - /* - * AHCI spec rev1.1 section 8.3.3: - * Software must disable interrupts prior to requesting a - * transition of the HBA to D3 state. - */ - ctl = readl(mmio + HOST_CTL); - ctl &= ~HOST_IRQ_EN; - writel(ctl, mmio + HOST_CTL); - readl(mmio + HOST_CTL); /* flush */ - - rc = ata_host_suspend(host, PMSG_SUSPEND); - if (rc) - return rc; - - if (pdata && pdata->suspend) - return pdata->suspend(dev); - - if (!IS_ERR(hpriv->clk)) - clk_disable_unprepare(hpriv->clk); - - return 0; -} - -static int ahci_resume(struct device *dev) -{ - struct ahci_platform_data *pdata = dev_get_platdata(dev); - struct ata_host *host = dev_get_drvdata(dev); - struct ahci_host_priv *hpriv = host->private_data; - int rc; - - if (!IS_ERR(hpriv->clk)) { - rc = clk_prepare_enable(hpriv->clk); - if (rc) { - dev_err(dev, "clock prepare enable failed"); - return rc; - } - } - - if (pdata && pdata->resume) { - rc = pdata->resume(dev); - if (rc) - goto disable_unprepare_clk; - } - - if (dev->power.power_state.event == PM_EVENT_SUSPEND) { - rc = ahci_reset_controller(host); - if (rc) - goto disable_unprepare_clk; - - ahci_init_controller(host); - } - - ata_host_resume(host); - - return 0; - -disable_unprepare_clk: - if (!IS_ERR(hpriv->clk)) - clk_disable_unprepare(hpriv->clk); - +disable_resources: + ahci_platform_disable_resources(hpriv); return rc; } -#endif -static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume); +static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, + ahci_platform_resume); static const struct of_device_id ahci_of_match[] = { { .compatible = "snps,spear-ahci", }, { .compatible = "snps,exynos5440-ahci", }, { .compatible = "ibm,476gtr-ahci", }, + { .compatible = "snps,dwc-ahci", }, {}, }; MODULE_DEVICE_TABLE(of, ahci_of_match); @@ -343,7 +89,6 @@ static struct platform_driver ahci_driver = { .of_match_table = ahci_of_match, .pm = &ahci_pm_ops, }, - .id_table = ahci_devtype, }; module_platform_driver(ahci_driver); diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c new file mode 100644 index 000000000000..633222226c19 --- /dev/null +++ b/drivers/ata/ahci_st.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2012 STMicroelectronics Limited + * + * Authors: Francesco Virlinzi <francesco.virlinzi@st.com> + * Alexandre Torgue <alexandre.torgue@st.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/init.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/ahci_platform.h> +#include <linux/libata.h> +#include <linux/reset.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> + +#include "ahci.h" + +#define ST_AHCI_OOBR 0xbc +#define ST_AHCI_OOBR_WE BIT(31) +#define ST_AHCI_OOBR_CWMIN_SHIFT 24 +#define ST_AHCI_OOBR_CWMAX_SHIFT 16 +#define ST_AHCI_OOBR_CIMIN_SHIFT 8 +#define ST_AHCI_OOBR_CIMAX_SHIFT 0 + +struct st_ahci_drv_data { + struct platform_device *ahci; + struct reset_control *pwr; + struct reset_control *sw_rst; + struct reset_control *pwr_rst; + struct ahci_host_priv *hpriv; +}; + +static void st_ahci_configure_oob(void __iomem *mmio) +{ + unsigned long old_val, new_val; + + new_val = (0x02 << ST_AHCI_OOBR_CWMIN_SHIFT) | + (0x04 << ST_AHCI_OOBR_CWMAX_SHIFT) | + (0x08 << ST_AHCI_OOBR_CIMIN_SHIFT) | + (0x0C << ST_AHCI_OOBR_CIMAX_SHIFT); + + old_val = readl(mmio + ST_AHCI_OOBR); + writel(old_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR); + writel(new_val | ST_AHCI_OOBR_WE, mmio + ST_AHCI_OOBR); + writel(new_val, mmio + ST_AHCI_OOBR); +} + +static int st_ahci_deassert_resets(struct device *dev) +{ + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + int err; + + if (drv_data->pwr) { + err = reset_control_deassert(drv_data->pwr); + if (err) { + dev_err(dev, "unable to bring out of pwrdwn\n"); + return err; + } + } + + st_ahci_configure_oob(drv_data->hpriv->mmio); + + if (drv_data->sw_rst) { + err = reset_control_deassert(drv_data->sw_rst); + if (err) { + dev_err(dev, "unable to bring out of sw-rst\n"); + return err; + } + } + + if (drv_data->pwr_rst) { + err = reset_control_deassert(drv_data->pwr_rst); + if (err) { + dev_err(dev, "unable to bring out of pwr-rst\n"); + return err; + } + } + + return 0; +} + +static void st_ahci_host_stop(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + struct device *dev = host->dev; + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + int err; + + if (drv_data->pwr) { + err = reset_control_assert(drv_data->pwr); + if (err) + dev_err(dev, "unable to pwrdwn\n"); + } + + ahci_platform_disable_resources(hpriv); +} + +static int st_ahci_probe_resets(struct platform_device *pdev) +{ + struct st_ahci_drv_data *drv_data = platform_get_drvdata(pdev); + + drv_data->pwr = devm_reset_control_get(&pdev->dev, "pwr-dwn"); + if (IS_ERR(drv_data->pwr)) { + dev_info(&pdev->dev, "power reset control not defined\n"); + drv_data->pwr = NULL; + } + + drv_data->sw_rst = devm_reset_control_get(&pdev->dev, "sw-rst"); + if (IS_ERR(drv_data->sw_rst)) { + dev_info(&pdev->dev, "soft reset control not defined\n"); + drv_data->sw_rst = NULL; + } + + drv_data->pwr_rst = devm_reset_control_get(&pdev->dev, "pwr-rst"); + if (IS_ERR(drv_data->pwr_rst)) { + dev_dbg(&pdev->dev, "power soft reset control not defined\n"); + drv_data->pwr_rst = NULL; + } + + return st_ahci_deassert_resets(&pdev->dev); +} + +static struct ata_port_operations st_ahci_port_ops = { + .inherits = &ahci_platform_ops, + .host_stop = st_ahci_host_stop, +}; + +static const struct ata_port_info st_ahci_port_info = { + .flags = AHCI_FLAG_COMMON, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &st_ahci_port_ops, +}; + +static int st_ahci_probe(struct platform_device *pdev) +{ + struct st_ahci_drv_data *drv_data; + struct ahci_host_priv *hpriv; + int err; + + drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + platform_set_drvdata(pdev, drv_data); + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + drv_data->hpriv = hpriv; + + err = st_ahci_probe_resets(pdev); + if (err) + return err; + + err = ahci_platform_enable_resources(hpriv); + if (err) + return err; + + err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, 0, 0); + if (err) { + ahci_platform_disable_resources(hpriv); + return err; + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int st_ahci_suspend(struct device *dev) +{ + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = drv_data->hpriv; + int err; + + err = ahci_platform_suspend_host(dev); + if (err) + return err; + + if (drv_data->pwr) { + err = reset_control_assert(drv_data->pwr); + if (err) { + dev_err(dev, "unable to pwrdwn"); + return err; + } + } + + ahci_platform_disable_resources(hpriv); + + return 0; +} + +static int st_ahci_resume(struct device *dev) +{ + struct st_ahci_drv_data *drv_data = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = drv_data->hpriv; + int err; + + err = ahci_platform_enable_resources(hpriv); + if (err) + return err; + + err = st_ahci_deassert_resets(dev); + if (err) { + ahci_platform_disable_resources(hpriv); + return err; + } + + return ahci_platform_resume_host(dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(st_ahci_pm_ops, st_ahci_suspend, st_ahci_resume); + +static struct of_device_id st_ahci_match[] = { + { .compatible = "st,ahci", }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_ahci_match); + +static struct platform_driver st_ahci_driver = { + .driver = { + .name = "st_ahci", + .owner = THIS_MODULE, + .pm = &st_ahci_pm_ops, + .of_match_table = of_match_ptr(st_ahci_match), + }, + .probe = st_ahci_probe, + .remove = ata_platform_remove_one, +}; +module_platform_driver(st_ahci_driver); + +MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>"); +MODULE_AUTHOR("Francesco Virlinzi <francesco.virlinzi@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics SATA AHCI Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c new file mode 100644 index 000000000000..42d3f64e74b3 --- /dev/null +++ b/drivers/ata/ahci_sunxi.c @@ -0,0 +1,249 @@ +/* + * Allwinner sunxi AHCI SATA platform driver + * Copyright 2013 Olliver Schinagl <oliver@schinagl.nl> + * Copyright 2014 Hans de Goede <hdegoede@redhat.com> + * + * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov + * Based on code from Allwinner Technology Co., Ltd. <www.allwinnertech.com>, + * Daniel Wang <danielwang@allwinnertech.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/ahci_platform.h> +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include "ahci.h" + +#define AHCI_BISTAFR 0x00a0 +#define AHCI_BISTCR 0x00a4 +#define AHCI_BISTFCTR 0x00a8 +#define AHCI_BISTSR 0x00ac +#define AHCI_BISTDECR 0x00b0 +#define AHCI_DIAGNR0 0x00b4 +#define AHCI_DIAGNR1 0x00b8 +#define AHCI_OOBR 0x00bc +#define AHCI_PHYCS0R 0x00c0 +#define AHCI_PHYCS1R 0x00c4 +#define AHCI_PHYCS2R 0x00c8 +#define AHCI_TIMER1MS 0x00e0 +#define AHCI_GPARAM1R 0x00e8 +#define AHCI_GPARAM2R 0x00ec +#define AHCI_PPARAMR 0x00f0 +#define AHCI_TESTR 0x00f4 +#define AHCI_VERSIONR 0x00f8 +#define AHCI_IDR 0x00fc +#define AHCI_RWCR 0x00fc +#define AHCI_P0DMACR 0x0170 +#define AHCI_P0PHYCR 0x0178 +#define AHCI_P0PHYSR 0x017c + +static void sunxi_clrbits(void __iomem *reg, u32 clr_val) +{ + u32 reg_val; + + reg_val = readl(reg); + reg_val &= ~(clr_val); + writel(reg_val, reg); +} + +static void sunxi_setbits(void __iomem *reg, u32 set_val) +{ + u32 reg_val; + + reg_val = readl(reg); + reg_val |= set_val; + writel(reg_val, reg); +} + +static void sunxi_clrsetbits(void __iomem *reg, u32 clr_val, u32 set_val) +{ + u32 reg_val; + + reg_val = readl(reg); + reg_val &= ~(clr_val); + reg_val |= set_val; + writel(reg_val, reg); +} + +static u32 sunxi_getbits(void __iomem *reg, u8 mask, u8 shift) +{ + return (readl(reg) >> shift) & mask; +} + +static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base) +{ + u32 reg_val; + int timeout; + + /* This magic is from the original code */ + writel(0, reg_base + AHCI_RWCR); + msleep(5); + + sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(19)); + sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, + (0x7 << 24), + (0x5 << 24) | BIT(23) | BIT(18)); + sunxi_clrsetbits(reg_base + AHCI_PHYCS1R, + (0x3 << 16) | (0x1f << 8) | (0x3 << 6), + (0x2 << 16) | (0x6 << 8) | (0x2 << 6)); + sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(28) | BIT(15)); + sunxi_clrbits(reg_base + AHCI_PHYCS1R, BIT(19)); + sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, + (0x7 << 20), (0x3 << 20)); + sunxi_clrsetbits(reg_base + AHCI_PHYCS2R, + (0x1f << 5), (0x19 << 5)); + msleep(5); + + sunxi_setbits(reg_base + AHCI_PHYCS0R, (0x1 << 19)); + + timeout = 250; /* Power up takes aprox 50 us */ + do { + reg_val = sunxi_getbits(reg_base + AHCI_PHYCS0R, 0x7, 28); + if (reg_val == 0x02) + break; + + if (--timeout == 0) { + dev_err(dev, "PHY power up failed.\n"); + return -EIO; + } + udelay(1); + } while (1); + + sunxi_setbits(reg_base + AHCI_PHYCS2R, (0x1 << 24)); + + timeout = 100; /* Calibration takes aprox 10 us */ + do { + reg_val = sunxi_getbits(reg_base + AHCI_PHYCS2R, 0x1, 24); + if (reg_val == 0x00) + break; + + if (--timeout == 0) { + dev_err(dev, "PHY calibration failed.\n"); + return -EIO; + } + udelay(1); + } while (1); + + msleep(15); + + writel(0x7, reg_base + AHCI_RWCR); + + return 0; +} + +static void ahci_sunxi_start_engine(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_host_priv *hpriv = ap->host->private_data; + + /* Setup DMA before DMA start */ + sunxi_clrsetbits(hpriv->mmio + AHCI_P0DMACR, 0x0000ff00, 0x00004400); + + /* Start DMA */ + sunxi_setbits(port_mmio + PORT_CMD, PORT_CMD_START); +} + +static const struct ata_port_info ahci_sunxi_port_info = { + AHCI_HFLAGS(AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | + AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), + .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &ahci_platform_ops, +}; + +static int ahci_sunxi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + int rc; + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + hpriv->start_engine = ahci_sunxi_start_engine; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + rc = ahci_sunxi_phy_init(dev, hpriv->mmio); + if (rc) + goto disable_resources; + + rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info, 0, 0); + if (rc) + goto disable_resources; + + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} + +#ifdef CONFIG_PM_SLEEP +static int ahci_sunxi_resume(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + rc = ahci_sunxi_phy_init(dev, hpriv->mmio); + if (rc) + goto disable_resources; + + rc = ahci_platform_resume_host(dev); + if (rc) + goto disable_resources; + + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} +#endif + +static SIMPLE_DEV_PM_OPS(ahci_sunxi_pm_ops, ahci_platform_suspend, + ahci_sunxi_resume); + +static const struct of_device_id ahci_sunxi_of_match[] = { + { .compatible = "allwinner,sun4i-a10-ahci", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ahci_sunxi_of_match); + +static struct platform_driver ahci_sunxi_driver = { + .probe = ahci_sunxi_probe, + .remove = ata_platform_remove_one, + .driver = { + .name = "ahci-sunxi", + .owner = THIS_MODULE, + .of_match_table = ahci_sunxi_of_match, + .pm = &ahci_sunxi_pm_ops, + }, +}; +module_platform_driver(ahci_sunxi_driver); + +MODULE_DESCRIPTION("Allwinner sunxi AHCI SATA driver"); +MODULE_AUTHOR("Olliver Schinagl <oliver@schinagl.nl>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c new file mode 100644 index 000000000000..77c89bf171f1 --- /dev/null +++ b/drivers/ata/ahci_xgene.c @@ -0,0 +1,486 @@ +/* + * AppliedMicro X-Gene SoC SATA Host Controller Driver + * + * Copyright (c) 2014, Applied Micro Circuits Corporation + * Author: Loc Ho <lho@apm.com> + * Tuan Phan <tphan@apm.com> + * Suman Tripathi <stripathi@apm.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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * NOTE: PM support is not currently available. + * + */ +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/ahci_platform.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/phy/phy.h> +#include "ahci.h" + +/* Max # of disk per a controller */ +#define MAX_AHCI_CHN_PERCTR 2 + +/* MUX CSR */ +#define SATA_ENET_CONFIG_REG 0x00000000 +#define CFG_SATA_ENET_SELECT_MASK 0x00000001 + +/* SATA core host controller CSR */ +#define SLVRDERRATTRIBUTES 0x00000000 +#define SLVWRERRATTRIBUTES 0x00000004 +#define MSTRDERRATTRIBUTES 0x00000008 +#define MSTWRERRATTRIBUTES 0x0000000c +#define BUSCTLREG 0x00000014 +#define IOFMSTRWAUX 0x00000018 +#define INTSTATUSMASK 0x0000002c +#define ERRINTSTATUS 0x00000030 +#define ERRINTSTATUSMASK 0x00000034 + +/* SATA host AHCI CSR */ +#define PORTCFG 0x000000a4 +#define PORTADDR_SET(dst, src) \ + (((dst) & ~0x0000003f) | (((u32)(src)) & 0x0000003f)) +#define PORTPHY1CFG 0x000000a8 +#define PORTPHY1CFG_FRCPHYRDY_SET(dst, src) \ + (((dst) & ~0x00100000) | (((u32)(src) << 0x14) & 0x00100000)) +#define PORTPHY2CFG 0x000000ac +#define PORTPHY3CFG 0x000000b0 +#define PORTPHY4CFG 0x000000b4 +#define PORTPHY5CFG 0x000000b8 +#define SCTL0 0x0000012C +#define PORTPHY5CFG_RTCHG_SET(dst, src) \ + (((dst) & ~0xfff00000) | (((u32)(src) << 0x14) & 0xfff00000)) +#define PORTAXICFG_EN_CONTEXT_SET(dst, src) \ + (((dst) & ~0x01000000) | (((u32)(src) << 0x18) & 0x01000000)) +#define PORTAXICFG 0x000000bc +#define PORTAXICFG_OUTTRANS_SET(dst, src) \ + (((dst) & ~0x00f00000) | (((u32)(src) << 0x14) & 0x00f00000)) + +/* SATA host controller AXI CSR */ +#define INT_SLV_TMOMASK 0x00000010 + +/* SATA diagnostic CSR */ +#define CFG_MEM_RAM_SHUTDOWN 0x00000070 +#define BLOCK_MEM_RDY 0x00000074 + +struct xgene_ahci_context { + struct ahci_host_priv *hpriv; + struct device *dev; + 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 */ + void __iomem *csr_mux; /* MUX CSR address of IP */ +}; + +static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx) +{ + dev_dbg(ctx->dev, "Release memory from shutdown\n"); + writel(0x0, ctx->csr_diag + CFG_MEM_RAM_SHUTDOWN); + readl(ctx->csr_diag + CFG_MEM_RAM_SHUTDOWN); /* Force a barrier */ + msleep(1); /* reset may take up to 1ms */ + if (readl(ctx->csr_diag + BLOCK_MEM_RDY) != 0xFFFFFFFF) { + dev_err(ctx->dev, "failed to release memory from shutdown\n"); + return -ENODEV; + } + return 0; +} + +/** + * xgene_ahci_read_id - Read ID data from the specified device + * @dev: device + * @tf: proposed taskfile + * @id: data buffer + * + * This custom read ID function is required due to the fact that the HW + * does not support DEVSLP and the controller state machine may get stuck + * after processing the ID query command. + */ +static unsigned int xgene_ahci_read_id(struct ata_device *dev, + struct ata_taskfile *tf, u16 *id) +{ + u32 err_mask; + void __iomem *port_mmio = ahci_port_base(dev->link->ap); + + err_mask = ata_do_dev_read_id(dev, tf, id); + if (err_mask) + return err_mask; + + /* + * Mask reserved area. Word78 spec of Link Power Management + * bit15-8: reserved + * bit7: NCQ autosence + * bit6: Software settings preservation supported + * bit5: reserved + * bit4: In-order sata delivery supported + * bit3: DIPM requests supported + * bit2: DMA Setup FIS Auto-Activate optimization supported + * bit1: DMA Setup FIX non-Zero buffer offsets supported + * bit0: Reserved + * + * Clear reserved bit 8 (DEVSLP bit) as we don't support DEVSLP + */ + id[ATA_ID_FEATURE_SUPP] &= ~(1 << 8); + + /* + * Due to HW errata, restart the port if no other command active. + * Otherwise the controller may get stuck. + */ + if (!readl(port_mmio + PORT_CMD_ISSUE)) { + writel(PORT_CMD_FIS_RX, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* Force a barrier */ + writel(PORT_CMD_FIS_RX | PORT_CMD_START, port_mmio + PORT_CMD); + readl(port_mmio + PORT_CMD); /* Force a barrier */ + } + return 0; +} + +static void xgene_ahci_set_phy_cfg(struct xgene_ahci_context *ctx, int channel) +{ + void __iomem *mmio = ctx->hpriv->mmio; + u32 val; + + dev_dbg(ctx->dev, "port configure mmio 0x%p channel %d\n", + mmio, channel); + val = readl(mmio + PORTCFG); + val = PORTADDR_SET(val, channel == 0 ? 2 : 3); + writel(val, mmio + PORTCFG); + readl(mmio + PORTCFG); /* Force a barrier */ + /* Disable fix rate */ + writel(0x0001fffe, mmio + PORTPHY1CFG); + readl(mmio + PORTPHY1CFG); /* Force a barrier */ + writel(0x5018461c, mmio + PORTPHY2CFG); + readl(mmio + PORTPHY2CFG); /* Force a barrier */ + writel(0x1c081907, mmio + PORTPHY3CFG); + readl(mmio + PORTPHY3CFG); /* Force a barrier */ + writel(0x1c080815, mmio + PORTPHY4CFG); + readl(mmio + PORTPHY4CFG); /* Force a barrier */ + /* Set window negotiation */ + val = readl(mmio + PORTPHY5CFG); + val = PORTPHY5CFG_RTCHG_SET(val, 0x300); + writel(val, mmio + PORTPHY5CFG); + readl(mmio + PORTPHY5CFG); /* Force a barrier */ + val = readl(mmio + PORTAXICFG); + val = PORTAXICFG_EN_CONTEXT_SET(val, 0x1); /* Enable context mgmt */ + val = PORTAXICFG_OUTTRANS_SET(val, 0xe); /* Set outstanding */ + writel(val, mmio + PORTAXICFG); + readl(mmio + PORTAXICFG); /* Force a barrier */ +} + +/** + * xgene_ahci_do_hardreset - Issue the actual COMRESET + * @link: link to reset + * @deadline: deadline jiffies for the operation + * @online: Return value to indicate if device online + * + * Due to the limitation of the hardware PHY, a difference set of setting is + * required for each supported disk speed - Gen3 (6.0Gbps), Gen2 (3.0Gbps), + * and Gen1 (1.5Gbps). Otherwise during long IO stress test, the PHY will + * report disparity error and etc. In addition, during COMRESET, there can + * be error reported in the register PORT_SCR_ERR. For SERR_DISPARITY and + * SERR_10B_8B_ERR, the PHY receiver line must be reseted. The following + * algorithm is followed to proper configure the hardware PHY during COMRESET: + * + * Alg Part 1: + * 1. Start the PHY at Gen3 speed (default setting) + * 2. Issue the COMRESET + * 3. If no link, go to Alg Part 3 + * 4. If link up, determine if the negotiated speed matches the PHY + * configured speed + * 5. If they matched, go to Alg Part 2 + * 6. If they do not matched and first time, configure the PHY for the linked + * up disk speed and repeat step 2 + * 7. Go to Alg Part 2 + * + * Alg Part 2: + * 1. On link up, if there are any SERR_DISPARITY and SERR_10B_8B_ERR error + * reported in the register PORT_SCR_ERR, then reset the PHY receiver line + * 2. Go to Alg Part 3 + * + * Alg Part 3: + * 1. Clear any pending from register PORT_SCR_ERR. + * + * NOTE: For the initial version, we will NOT support Gen1/Gen2. In addition + * and until the underlying PHY supports an method to reset the receiver + * line, on detection of SERR_DISPARITY or SERR_10B_8B_ERR errors, + * an warning message will be printed. + */ +static int xgene_ahci_do_hardreset(struct ata_link *link, + unsigned long deadline, bool *online) +{ + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; + struct xgene_ahci_context *ctx = hpriv->plat_data; + struct ahci_port_priv *pp = ap->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_taskfile tf; + int rc; + u32 val; + + /* clear D2H reception area to properly wait for D2H FIS */ + ata_tf_init(link->device, &tf); + tf.command = ATA_BUSY; + ata_tf_to_fis(&tf, 0, 0, d2h_fis); + rc = sata_link_hardreset(link, timing, deadline, online, + ahci_check_ready); + + val = readl(port_mmio + PORT_SCR_ERR); + if (val & (SERR_DISPARITY | SERR_10B_8B_ERR)) + dev_warn(ctx->dev, "link has error\n"); + + /* clear all errors if any pending */ + val = readl(port_mmio + PORT_SCR_ERR); + writel(val, port_mmio + PORT_SCR_ERR); + + return rc; +} + +static int xgene_ahci_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + bool online; + int rc; + u32 portcmd_saved; + u32 portclb_saved; + u32 portclbhi_saved; + u32 portrxfis_saved; + u32 portrxfishi_saved; + + /* As hardreset resets these CSR, save it to restore later */ + portcmd_saved = readl(port_mmio + PORT_CMD); + portclb_saved = readl(port_mmio + PORT_LST_ADDR); + portclbhi_saved = readl(port_mmio + PORT_LST_ADDR_HI); + portrxfis_saved = readl(port_mmio + PORT_FIS_ADDR); + portrxfishi_saved = readl(port_mmio + PORT_FIS_ADDR_HI); + + ahci_stop_engine(ap); + + rc = xgene_ahci_do_hardreset(link, deadline, &online); + + /* As controller hardreset clears them, restore them */ + writel(portcmd_saved, port_mmio + PORT_CMD); + writel(portclb_saved, port_mmio + PORT_LST_ADDR); + writel(portclbhi_saved, port_mmio + PORT_LST_ADDR_HI); + writel(portrxfis_saved, port_mmio + PORT_FIS_ADDR); + writel(portrxfishi_saved, port_mmio + PORT_FIS_ADDR_HI); + + hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); + + return rc; +} + +static void xgene_ahci_host_stop(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + + ahci_platform_disable_resources(hpriv); +} + +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, +}; + +static const struct ata_port_info xgene_ahci_port_info = { + AHCI_HFLAGS(AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), + .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ, + .pio_mask = ATA_PIO4, + .udma_mask = ATA_UDMA6, + .port_ops = &xgene_ahci_ops, +}; + +static int xgene_ahci_hw_init(struct ahci_host_priv *hpriv) +{ + struct xgene_ahci_context *ctx = hpriv->plat_data; + int i; + int rc; + u32 val; + + /* Remove IP RAM out of shutdown */ + rc = xgene_ahci_init_memram(ctx); + if (rc) + return rc; + + for (i = 0; i < MAX_AHCI_CHN_PERCTR; i++) + xgene_ahci_set_phy_cfg(ctx, i); + + /* AXI disable Mask */ + writel(0xffffffff, hpriv->mmio + HOST_IRQ_STAT); + readl(hpriv->mmio + HOST_IRQ_STAT); /* Force a barrier */ + writel(0, ctx->csr_core + INTSTATUSMASK); + val = readl(ctx->csr_core + INTSTATUSMASK); /* Force a barrier */ + dev_dbg(ctx->dev, "top level interrupt mask 0x%X value 0x%08X\n", + INTSTATUSMASK, val); + + writel(0x0, ctx->csr_core + ERRINTSTATUSMASK); + readl(ctx->csr_core + ERRINTSTATUSMASK); /* Force a barrier */ + writel(0x0, ctx->csr_axi + INT_SLV_TMOMASK); + readl(ctx->csr_axi + INT_SLV_TMOMASK); + + /* Enable AXI Interrupt */ + writel(0xffffffff, ctx->csr_core + SLVRDERRATTRIBUTES); + writel(0xffffffff, ctx->csr_core + SLVWRERRATTRIBUTES); + writel(0xffffffff, ctx->csr_core + MSTRDERRATTRIBUTES); + writel(0xffffffff, ctx->csr_core + MSTWRERRATTRIBUTES); + + /* Enable coherency */ + val = readl(ctx->csr_core + BUSCTLREG); + val &= ~0x00000002; /* Enable write coherency */ + val &= ~0x00000001; /* Enable read coherency */ + writel(val, ctx->csr_core + BUSCTLREG); + + val = readl(ctx->csr_core + IOFMSTRWAUX); + val |= (1 << 3); /* Enable read coherency */ + val |= (1 << 9); /* Enable write coherency */ + writel(val, ctx->csr_core + IOFMSTRWAUX); + val = readl(ctx->csr_core + IOFMSTRWAUX); + dev_dbg(ctx->dev, "coherency 0x%X value 0x%08X\n", + IOFMSTRWAUX, val); + + return rc; +} + +static int xgene_ahci_mux_select(struct xgene_ahci_context *ctx) +{ + u32 val; + + /* Check for optional MUX resource */ + if (IS_ERR(ctx->csr_mux)) + return 0; + + val = readl(ctx->csr_mux + SATA_ENET_CONFIG_REG); + val &= ~CFG_SATA_ENET_SELECT_MASK; + writel(val, ctx->csr_mux + SATA_ENET_CONFIG_REG); + val = readl(ctx->csr_mux + SATA_ENET_CONFIG_REG); + return val & CFG_SATA_ENET_SELECT_MASK ? -1 : 0; +} + +static int xgene_ahci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + struct xgene_ahci_context *ctx; + struct resource *res; + int rc; + + hpriv = ahci_platform_get_resources(pdev); + if (IS_ERR(hpriv)) + return PTR_ERR(hpriv); + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + hpriv->plat_data = ctx; + ctx->hpriv = hpriv; + ctx->dev = dev; + + /* Retrieve the IP core resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + ctx->csr_core = devm_ioremap_resource(dev, res); + if (IS_ERR(ctx->csr_core)) + return PTR_ERR(ctx->csr_core); + + /* Retrieve the IP diagnostic resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + ctx->csr_diag = devm_ioremap_resource(dev, res); + if (IS_ERR(ctx->csr_diag)) + return PTR_ERR(ctx->csr_diag); + + /* Retrieve the IP AXI resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + ctx->csr_axi = devm_ioremap_resource(dev, res); + if (IS_ERR(ctx->csr_axi)) + return PTR_ERR(ctx->csr_axi); + + /* Retrieve the optional IP mux resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + ctx->csr_mux = devm_ioremap_resource(dev, res); + + dev_dbg(dev, "VAddr 0x%p Mmio VAddr 0x%p\n", ctx->csr_core, + hpriv->mmio); + + /* Select ATA */ + if ((rc = xgene_ahci_mux_select(ctx))) { + dev_err(dev, "SATA mux selection failed error %d\n", rc); + return -ENODEV; + } + + /* Due to errata, HW requires full toggle transition */ + rc = ahci_platform_enable_clks(hpriv); + if (rc) + goto disable_resources; + ahci_platform_disable_clks(hpriv); + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + goto disable_resources; + + /* Configure the host controller */ + xgene_ahci_hw_init(hpriv); + + /* + * Setup DMA mask. This is preliminary until the DMA range is sorted + * out. + */ + rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (rc) { + dev_err(dev, "Unable to set dma mask\n"); + goto disable_resources; + } + + rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info, 0, 0); + if (rc) + goto disable_resources; + + dev_dbg(dev, "X-Gene SATA host controller initialized\n"); + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + return rc; +} + +static const struct of_device_id xgene_ahci_of_match[] = { + {.compatible = "apm,xgene-ahci"}, + {}, +}; +MODULE_DEVICE_TABLE(of, xgene_ahci_of_match); + +static struct platform_driver xgene_ahci_driver = { + .probe = xgene_ahci_probe, + .remove = ata_platform_remove_one, + .driver = { + .name = "xgene-ahci", + .owner = THIS_MODULE, + .of_match_table = xgene_ahci_of_match, + }, +}; + +module_platform_driver(xgene_ahci_driver); + +MODULE_DESCRIPTION("APM X-Gene AHCI SATA driver"); +MODULE_AUTHOR("Loc Ho <lho@apm.com>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.4"); diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c index 7d196656adb5..9498a7d3846f 100644 --- a/drivers/ata/ata_generic.c +++ b/drivers/ata/ata_generic.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 36605abe5a67..6bd4f660b4e1 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -35,7 +35,6 @@ #include <linux/kernel.h> #include <linux/gfp.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -394,6 +393,9 @@ static ssize_t ahci_show_em_supported(struct device *dev, * * If inconsistent, config values are fixed up by this function. * + * If it is not set already this function sets hpriv->start_engine to + * ahci_start_engine. + * * LOCKING: * None. */ @@ -500,6 +502,9 @@ void ahci_save_initial_config(struct device *dev, hpriv->cap = cap; hpriv->cap2 = cap2; hpriv->port_map = port_map; + + if (!hpriv->start_engine) + hpriv->start_engine = ahci_start_engine; } EXPORT_SYMBOL_GPL(ahci_save_initial_config); @@ -766,7 +771,7 @@ static void ahci_start_port(struct ata_port *ap) /* enable DMA */ if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE)) - ahci_start_engine(ap); + hpriv->start_engine(ap); /* turn on LEDs */ if (ap->flags & ATA_FLAG_EM) { @@ -1032,12 +1037,13 @@ static ssize_t ahci_led_show(struct ata_port *ap, char *buf) static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, size_t size) { - int state; + unsigned int state; int pmp; struct ahci_port_priv *pp = ap->private_data; struct ahci_em_priv *emp; - state = simple_strtoul(buf, NULL, 0); + if (kstrtouint(buf, 0, &state) < 0) + return -EINVAL; /* get the slot number from the message */ pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8; @@ -1234,7 +1240,7 @@ int ahci_kick_engine(struct ata_port *ap) /* restart engine */ out_restart: - ahci_start_engine(ap); + hpriv->start_engine(ap); return rc; } EXPORT_SYMBOL_GPL(ahci_kick_engine); @@ -1387,8 +1393,8 @@ static int ahci_bad_pmp_check_ready(struct ata_link *link) return ata_check_ready(status); } -int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class, - unsigned long deadline) +static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) { struct ata_port *ap = link->ap; void __iomem *port_mmio = ahci_port_base(ap); @@ -1426,6 +1432,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; bool online; @@ -1443,7 +1450,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, rc = sata_link_hardreset(link, timing, deadline, &online, ahci_check_ready); - ahci_start_engine(ap); + hpriv->start_engine(ap); if (online) *class = ahci_dev_classify(ap); @@ -1629,7 +1636,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) } if (irq_stat & PORT_IRQ_UNK_FIS) { - u32 *unk = (u32 *)(pp->rx_fis + RX_FIS_UNK); + u32 *unk = pp->rx_fis + RX_FIS_UNK; active_ehi->err_mask |= AC_ERR_HSM; active_ehi->action |= ATA_EH_RESET; @@ -2007,10 +2014,12 @@ static void ahci_thaw(struct ata_port *ap) void ahci_error_handler(struct ata_port *ap) { + struct ahci_host_priv *hpriv = ap->host->private_data; + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { /* restart engine */ ahci_stop_engine(ap); - ahci_start_engine(ap); + hpriv->start_engine(ap); } sata_pmp_error_handler(ap); @@ -2031,6 +2040,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) { + struct ahci_host_priv *hpriv = ap->host->private_data; void __iomem *port_mmio = ahci_port_base(ap); struct ata_device *dev = ap->link.device; u32 devslp, dm, dito, mdat, deto; @@ -2094,7 +2104,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) PORT_DEVSLP_ADSE); writel(devslp, port_mmio + PORT_DEVSLP); - ahci_start_engine(ap); + hpriv->start_engine(ap); /* enable device sleep feature for the drive */ err_mask = ata_dev_set_feature(dev, @@ -2106,6 +2116,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) static void ahci_enable_fbs(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; @@ -2134,11 +2145,12 @@ static void ahci_enable_fbs(struct ata_port *ap) } else dev_err(ap->host->dev, "Failed to enable FBS\n"); - ahci_start_engine(ap); + hpriv->start_engine(ap); } static void ahci_disable_fbs(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; @@ -2166,7 +2178,7 @@ static void ahci_disable_fbs(struct ata_port *ap) pp->fbs_enabled = false; } - ahci_start_engine(ap); + hpriv->start_engine(ap); } static void ahci_pmp_attach(struct ata_port *ap) diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c new file mode 100644 index 000000000000..7cb3a85719c0 --- /dev/null +++ b/drivers/ata/libahci_platform.c @@ -0,0 +1,541 @@ +/* + * AHCI SATA platform library + * + * Copyright 2004-2005 Red Hat, Inc. + * Jeff Garzik <jgarzik@pobox.com> + * Copyright 2010 MontaVista Software, LLC. + * Anton Vorontsov <avorontsov@ru.mvista.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, or (at your option) + * any later version. + */ + +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/libata.h> +#include <linux/ahci_platform.h> +#include <linux/phy/phy.h> +#include <linux/pm_runtime.h> +#include "ahci.h" + +static void ahci_host_stop(struct ata_host *host); + +struct ata_port_operations ahci_platform_ops = { + .inherits = &ahci_ops, + .host_stop = ahci_host_stop, +}; +EXPORT_SYMBOL_GPL(ahci_platform_ops); + +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT("ahci_platform"), +}; + +/** + * ahci_platform_enable_clks - Enable platform clocks + * @hpriv: host private area to store config values + * + * This function enables all the clks found in hpriv->clks, starting at + * index 0. If any clk fails to enable it disables all the clks already + * enabled in reverse order, and then returns an error. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) +{ + int c, rc; + + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { + rc = clk_prepare_enable(hpriv->clks[c]); + if (rc) + goto disable_unprepare_clk; + } + return 0; + +disable_unprepare_clk: + while (--c >= 0) + clk_disable_unprepare(hpriv->clks[c]); + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); + +/** + * ahci_platform_disable_clks - Disable platform clocks + * @hpriv: host private area to store config values + * + * This function disables all the clks found in hpriv->clks, in reverse + * order of ahci_platform_enable_clks (starting at the end of the array). + */ +void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) +{ + int c; + + for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) + if (hpriv->clks[c]) + clk_disable_unprepare(hpriv->clks[c]); +} +EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); + +/** + * ahci_platform_enable_resources - Enable platform resources + * @hpriv: host private area to store config values + * + * This function enables all ahci_platform managed resources in the + * following order: + * 1) Regulator + * 2) Clocks (through ahci_platform_enable_clks) + * 3) Phy + * + * If resource enabling fails at any point the previous enabled resources + * are disabled in reverse order. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +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_clks(hpriv); + if (rc) + goto disable_regulator; + + if (hpriv->phy) { + rc = phy_init(hpriv->phy); + if (rc) + goto disable_clks; + + rc = phy_power_on(hpriv->phy); + if (rc) { + phy_exit(hpriv->phy); + goto disable_clks; + } + } + + return 0; + +disable_clks: + ahci_platform_disable_clks(hpriv); + +disable_regulator: + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); + +/** + * ahci_platform_disable_resources - Disable platform resources + * @hpriv: host private area to store config values + * + * This function disables all ahci_platform managed resources in the + * following order: + * 1) Phy + * 2) Clocks (through ahci_platform_disable_clks) + * 3) Regulator + */ +void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) +{ + if (hpriv->phy) { + phy_power_off(hpriv->phy); + phy_exit(hpriv->phy); + } + + ahci_platform_disable_clks(hpriv); + + if (hpriv->target_pwr) + regulator_disable(hpriv->target_pwr); +} +EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); + +static void ahci_platform_put_resources(struct device *dev, void *res) +{ + struct ahci_host_priv *hpriv = res; + int c; + + if (hpriv->got_runtime_pm) { + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + } + + for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) + clk_put(hpriv->clks[c]); +} + +/** + * ahci_platform_get_resources - Get platform resources + * @pdev: platform device to get resources for + * + * This function allocates an ahci_host_priv struct, and gets the following + * resources, storing a reference to them inside the returned struct: + * + * 1) mmio registers (IORESOURCE_MEM 0, mandatory) + * 2) regulator for controlling the targets power (optional) + * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, + * or for non devicetree enabled platforms a single clock + * 4) phy (optional) + * + * RETURNS: + * The allocated ahci_host_priv on success, otherwise an ERR_PTR value + */ +struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ahci_host_priv *hpriv; + struct clk *clk; + int i, rc = -ENOMEM; + + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return ERR_PTR(-ENOMEM); + + hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), + GFP_KERNEL); + if (!hpriv) + goto err_out; + + devres_add(dev, hpriv); + + hpriv->mmio = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); + if (IS_ERR(hpriv->mmio)) { + dev_err(dev, "no mmio space\n"); + rc = PTR_ERR(hpriv->mmio); + 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, + * because some platforms (da850, spear13xx) are not yet + * converted to use devicetree for clocks. For new platforms + * this is equivalent to of_clk_get(dev->of_node, 0). + */ + if (i == 0) + clk = clk_get(dev, NULL); + else + clk = of_clk_get(dev->of_node, i); + + if (IS_ERR(clk)) { + rc = PTR_ERR(clk); + if (rc == -EPROBE_DEFER) + goto err_out; + break; + } + hpriv->clks[i] = clk; + } + + hpriv->phy = devm_phy_get(dev, "sata-phy"); + if (IS_ERR(hpriv->phy)) { + rc = PTR_ERR(hpriv->phy); + switch (rc) { + case -ENODEV: + case -ENOSYS: + /* continue normally */ + hpriv->phy = NULL; + break; + + case -EPROBE_DEFER: + goto err_out; + + default: + dev_err(dev, "couldn't get sata-phy\n"); + goto err_out; + } + } + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + hpriv->got_runtime_pm = true; + + devres_remove_group(dev, NULL); + return hpriv; + +err_out: + devres_release_group(dev, NULL); + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(ahci_platform_get_resources); + +/** + * ahci_platform_init_host - Bring up an ahci-platform host + * @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 + * @force_port_map: param passed to ahci_save_initial_config + * @mask_port_map: param passed to ahci_save_initial_config + * + * This function does all the usual steps needed to bring up an + * ahci-platform host, note any necessary resources (ie clks, phy, etc.) + * must be initialized / enabled before calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_init_host(struct platform_device *pdev, + struct ahci_host_priv *hpriv, + const struct ata_port_info *pi_template, + unsigned int force_port_map, + unsigned int mask_port_map) +{ + struct device *dev = &pdev->dev; + struct ata_port_info pi = *pi_template; + const struct ata_port_info *ppi[] = { &pi, NULL }; + struct ata_host *host; + int i, irq, n_ports, rc; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "no irq\n"); + return -EINVAL; + } + + /* prepare host */ + hpriv->flags |= (unsigned long)pi.private_data; + + ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); + + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + + if (hpriv->cap & HOST_CAP_PMP) + pi.flags |= ATA_FLAG_PMP; + + ahci_set_em_messages(hpriv, &pi); + + /* CAP.NP sometimes indicate the index of the last enabled + * port, at other times, that of the last possible port, so + * determining the maximum port number requires looking at + * both CAP.NP and port_map. + */ + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); + if (!host) + return -ENOMEM; + + host->private_data = hpriv; + + if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) + host->flags |= ATA_HOST_PARALLEL_SCAN; + else + dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); + + if (pi.flags & ATA_FLAG_EM) + ahci_reset_em(host); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + ata_port_desc(ap, "mmio %pR", + platform_get_resource(pdev, IORESOURCE_MEM, 0)); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set enclosure management message type */ + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = hpriv->em_msg_type; + + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; + } + + rc = ahci_reset_controller(host); + if (rc) + return rc; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + + return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, + &ahci_platform_sht); +} +EXPORT_SYMBOL_GPL(ahci_platform_init_host); + +static void ahci_host_stop(struct ata_host *host) +{ + struct device *dev = host->dev; + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + + if (pdata && pdata->exit) + pdata->exit(dev); + + ahci_platform_disable_resources(hpriv); +} + +#ifdef CONFIG_PM_SLEEP +/** + * ahci_platform_suspend_host - Suspend an ahci-platform host + * @dev: device pointer for the host + * + * This function does all the usual steps needed to suspend an + * ahci-platform host, note any necessary resources (ie clks, phy, etc.) + * must be disabled after calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_suspend_host(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 ctl; + + if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { + dev_err(dev, "firmware update required for suspend/resume\n"); + return -EIO; + } + + /* + * AHCI spec rev1.1 section 8.3.3: + * Software must disable interrupts prior to requesting a + * transition of the HBA to D3 state. + */ + ctl = readl(mmio + HOST_CTL); + ctl &= ~HOST_IRQ_EN; + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + + return ata_host_suspend(host, PMSG_SUSPEND); +} +EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); + +/** + * ahci_platform_resume_host - Resume an ahci-platform host + * @dev: device pointer for the host + * + * This function does all the usual steps needed to resume an ahci-platform + * host, note any necessary resources (ie clks, phy, etc.) must be + * initialized / enabled before calling this. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_resume_host(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + int rc; + + if (dev->power.power_state.event == PM_EVENT_SUSPEND) { + rc = ahci_reset_controller(host); + if (rc) + return rc; + + ahci_init_controller(host); + } + + ata_host_resume(host); + + return 0; +} +EXPORT_SYMBOL_GPL(ahci_platform_resume_host); + +/** + * ahci_platform_suspend - Suspend an ahci-platform device + * @dev: the platform device to suspend + * + * This function suspends the host associated with the device, followed by + * disabling all the resources of the device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_suspend(struct device *dev) +{ + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + + rc = ahci_platform_suspend_host(dev); + if (rc) + return rc; + + if (pdata && pdata->suspend) { + rc = pdata->suspend(dev); + if (rc) + goto resume_host; + } + + ahci_platform_disable_resources(hpriv); + + return 0; + +resume_host: + ahci_platform_resume_host(dev); + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_suspend); + +/** + * ahci_platform_resume - Resume an ahci-platform device + * @dev: the platform device to resume + * + * This function enables all the resources of the device followed by + * resuming the host associated with the device. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_resume(struct device *dev) +{ + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + + rc = ahci_platform_enable_resources(hpriv); + if (rc) + return rc; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); + if (rc) + goto disable_resources; + } + + rc = ahci_platform_resume_host(dev); + if (rc) + goto disable_resources; + + /* We resumed so update PM runtime state */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; + +disable_resources: + ahci_platform_disable_resources(hpriv); + + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_resume); +#endif + +MODULE_DESCRIPTION("AHCI SATA platform library"); +MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 9e69a5308693..97a14fe47de1 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -38,6 +38,16 @@ static void ata_acpi_clear_gtf(struct ata_device *dev) dev->gtf_cache = NULL; } +struct ata_acpi_hotplug_context { + struct acpi_hotplug_context hp; + union { + struct ata_port *ap; + struct ata_device *dev; + } data; +}; + +#define ata_hotplug_data(context) (container_of((context), struct ata_acpi_hotplug_context, hp)->data) + /** * ata_dev_acpi_handle - provide the acpi_handle for an ata_device * @dev: the acpi_handle returned will correspond to this device @@ -121,18 +131,17 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, ata_port_wait_eh(ap); } -static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data) +static int ata_acpi_dev_notify_dock(struct acpi_device *adev, u32 event) { - struct ata_device *dev = data; - + struct ata_device *dev = ata_hotplug_data(adev->hp).dev; ata_acpi_handle_hotplug(dev->link->ap, dev, event); + return 0; } -static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data) +static int ata_acpi_ap_notify_dock(struct acpi_device *adev, u32 event) { - struct ata_port *ap = data; - - ata_acpi_handle_hotplug(ap, NULL, event); + ata_acpi_handle_hotplug(ata_hotplug_data(adev->hp).ap, NULL, event); + return 0; } static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, @@ -154,31 +163,23 @@ static void ata_acpi_uevent(struct ata_port *ap, struct ata_device *dev, } } -static void ata_acpi_ap_uevent(acpi_handle handle, u32 event, void *data) +static void ata_acpi_ap_uevent(struct acpi_device *adev, u32 event) { - ata_acpi_uevent(data, NULL, event); + ata_acpi_uevent(ata_hotplug_data(adev->hp).ap, NULL, event); } -static void ata_acpi_dev_uevent(acpi_handle handle, u32 event, void *data) +static void ata_acpi_dev_uevent(struct acpi_device *adev, u32 event) { - struct ata_device *dev = data; + struct ata_device *dev = ata_hotplug_data(adev->hp).dev; ata_acpi_uevent(dev->link->ap, dev, event); } -static const struct acpi_dock_ops ata_acpi_dev_dock_ops = { - .handler = ata_acpi_dev_notify_dock, - .uevent = ata_acpi_dev_uevent, -}; - -static const struct acpi_dock_ops ata_acpi_ap_dock_ops = { - .handler = ata_acpi_ap_notify_dock, - .uevent = ata_acpi_ap_uevent, -}; - /* bind acpi handle to pata port */ void ata_acpi_bind_port(struct ata_port *ap) { struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); + struct acpi_device *adev; + struct ata_acpi_hotplug_context *context; if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_companion) return; @@ -188,9 +189,17 @@ void ata_acpi_bind_port(struct ata_port *ap) if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; - /* we might be on a docking station */ - register_hotplug_dock_device(ACPI_HANDLE(&ap->tdev), - &ata_acpi_ap_dock_ops, ap, NULL, NULL); + adev = ACPI_COMPANION(&ap->tdev); + if (!adev || adev->hp) + return; + + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return; + + context->data.ap = ap; + acpi_initialize_hp_context(adev, &context->hp, ata_acpi_ap_notify_dock, + ata_acpi_ap_uevent); } void ata_acpi_bind_dev(struct ata_device *dev) @@ -198,7 +207,8 @@ void ata_acpi_bind_dev(struct ata_device *dev) struct ata_port *ap = dev->link->ap; struct acpi_device *port_companion = ACPI_COMPANION(&ap->tdev); struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev); - struct acpi_device *parent; + struct acpi_device *parent, *adev; + struct ata_acpi_hotplug_context *context; u64 adr; /* @@ -221,9 +231,17 @@ void ata_acpi_bind_dev(struct ata_device *dev) } acpi_preset_companion(&dev->tdev, parent, adr); + adev = ACPI_COMPANION(&dev->tdev); + if (!adev || adev->hp) + return; + + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) + return; - register_hotplug_dock_device(ata_dev_acpi_handle(dev), - &ata_acpi_dev_dock_ops, dev, NULL, NULL); + context->data.dev = dev; + acpi_initialize_hp_context(adev, &context->hp, ata_acpi_dev_notify_dock, + ata_acpi_dev_uevent); } /** @@ -835,6 +853,7 @@ void ata_acpi_on_resume(struct ata_port *ap) ata_for_each_dev(dev, &ap->link, ALL) { ata_acpi_clear_gtf(dev); if (ata_dev_enabled(dev) && + ata_dev_acpi_handle(dev) && ata_dev_get_GTF(dev, NULL) >= 0) dev->flags |= ATA_DFLAG_ACPI_PENDING; } diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 1a3dbd1b196e..34406f7fdd7a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4175,6 +4175,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { /* Seagate Momentus SpinPoint M8 seem to have FPMDA_AA issues */ { "ST1000LM024 HN-M101MBB", "2AR10001", ATA_HORKAGE_BROKEN_FPDMA_AA }, + { "ST1000LM024 HN-M101MBB", "2BA30001", ATA_HORKAGE_BROKEN_FPDMA_AA }, /* Blacklist entries taken from Silicon Image 3124/3132 Windows driver .inf file - also several Linux problem reports */ @@ -4224,7 +4225,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { /* devices that don't properly handle queued TRIM commands */ { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, - { "Crucial_CT???M500SSD1", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, + { "Crucial_CT???M500SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, /* * Some WD SATA-I drives spin up and down erratically when the link @@ -5351,22 +5352,17 @@ bool ata_link_offline(struct ata_link *link) } #ifdef CONFIG_PM -static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, - unsigned int action, unsigned int ehi_flags, - int *async) +static void ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, + unsigned int action, unsigned int ehi_flags, + bool async) { struct ata_link *link; unsigned long flags; - int rc = 0; /* Previous resume operation might still be in * progress. Wait for PM_PENDING to clear. */ if (ap->pflags & ATA_PFLAG_PM_PENDING) { - if (async) { - *async = -EAGAIN; - return 0; - } ata_port_wait_eh(ap); WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); } @@ -5375,11 +5371,6 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, spin_lock_irqsave(ap->lock, flags); ap->pm_mesg = mesg; - if (async) - ap->pm_result = async; - else - ap->pm_result = &rc; - ap->pflags |= ATA_PFLAG_PM_PENDING; ata_for_each_link(link, ap, HOST_FIRST) { link->eh_info.action |= action; @@ -5390,87 +5381,81 @@ static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, spin_unlock_irqrestore(ap->lock, flags); - /* wait and check result */ if (!async) { ata_port_wait_eh(ap); WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); } - - return rc; } -static int __ata_port_suspend_common(struct ata_port *ap, pm_message_t mesg, int *async) +/* + * On some hardware, device fails to respond after spun down for suspend. As + * the device won't be used before being resumed, we don't need to touch the + * device. Ask EH to skip the usual stuff and proceed directly to suspend. + * + * http://thread.gmane.org/gmane.linux.ide/46764 + */ +static const unsigned int ata_port_suspend_ehi = ATA_EHI_QUIET + | ATA_EHI_NO_AUTOPSY + | ATA_EHI_NO_RECOVERY; + +static void ata_port_suspend(struct ata_port *ap, pm_message_t mesg) { - /* - * On some hardware, device fails to respond after spun down - * for suspend. As the device won't be used before being - * resumed, we don't need to touch the device. Ask EH to skip - * the usual stuff and proceed directly to suspend. - * - * http://thread.gmane.org/gmane.linux.ide/46764 - */ - unsigned int ehi_flags = ATA_EHI_QUIET | ATA_EHI_NO_AUTOPSY | - ATA_EHI_NO_RECOVERY; - return ata_port_request_pm(ap, mesg, 0, ehi_flags, async); + ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, false); } -static int ata_port_suspend_common(struct device *dev, pm_message_t mesg) +static void ata_port_suspend_async(struct ata_port *ap, pm_message_t mesg) { - struct ata_port *ap = to_ata_port(dev); - - return __ata_port_suspend_common(ap, mesg, NULL); + ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, true); } -static int ata_port_suspend(struct device *dev) +static int ata_port_pm_suspend(struct device *dev) { + struct ata_port *ap = to_ata_port(dev); + if (pm_runtime_suspended(dev)) return 0; - return ata_port_suspend_common(dev, PMSG_SUSPEND); + ata_port_suspend(ap, PMSG_SUSPEND); + return 0; } -static int ata_port_do_freeze(struct device *dev) +static int ata_port_pm_freeze(struct device *dev) { + struct ata_port *ap = to_ata_port(dev); + if (pm_runtime_suspended(dev)) return 0; - return ata_port_suspend_common(dev, PMSG_FREEZE); + ata_port_suspend(ap, PMSG_FREEZE); + return 0; } -static int ata_port_poweroff(struct device *dev) +static int ata_port_pm_poweroff(struct device *dev) { - return ata_port_suspend_common(dev, PMSG_HIBERNATE); + ata_port_suspend(to_ata_port(dev), PMSG_HIBERNATE); + return 0; } -static int __ata_port_resume_common(struct ata_port *ap, pm_message_t mesg, - int *async) -{ - int rc; +static const unsigned int ata_port_resume_ehi = ATA_EHI_NO_AUTOPSY + | ATA_EHI_QUIET; - rc = ata_port_request_pm(ap, mesg, ATA_EH_RESET, - ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, async); - return rc; +static void ata_port_resume(struct ata_port *ap, pm_message_t mesg) +{ + ata_port_request_pm(ap, mesg, ATA_EH_RESET, ata_port_resume_ehi, false); } -static int ata_port_resume_common(struct device *dev, pm_message_t mesg) +static void ata_port_resume_async(struct ata_port *ap, pm_message_t mesg) { - struct ata_port *ap = to_ata_port(dev); - - return __ata_port_resume_common(ap, mesg, NULL); + ata_port_request_pm(ap, mesg, ATA_EH_RESET, ata_port_resume_ehi, true); } -static int ata_port_resume(struct device *dev) +static int ata_port_pm_resume(struct device *dev) { - int rc; - - rc = ata_port_resume_common(dev, PMSG_RESUME); - if (!rc) { - pm_runtime_disable(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - } - - return rc; + ata_port_resume_async(to_ata_port(dev), PMSG_RESUME); + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; } /* @@ -5499,21 +5484,23 @@ static int ata_port_runtime_idle(struct device *dev) static int ata_port_runtime_suspend(struct device *dev) { - return ata_port_suspend_common(dev, PMSG_AUTO_SUSPEND); + ata_port_suspend(to_ata_port(dev), PMSG_AUTO_SUSPEND); + return 0; } static int ata_port_runtime_resume(struct device *dev) { - return ata_port_resume_common(dev, PMSG_AUTO_RESUME); + ata_port_resume(to_ata_port(dev), PMSG_AUTO_RESUME); + return 0; } static const struct dev_pm_ops ata_port_pm_ops = { - .suspend = ata_port_suspend, - .resume = ata_port_resume, - .freeze = ata_port_do_freeze, - .thaw = ata_port_resume, - .poweroff = ata_port_poweroff, - .restore = ata_port_resume, + .suspend = ata_port_pm_suspend, + .resume = ata_port_pm_resume, + .freeze = ata_port_pm_freeze, + .thaw = ata_port_pm_resume, + .poweroff = ata_port_pm_poweroff, + .restore = ata_port_pm_resume, .runtime_suspend = ata_port_runtime_suspend, .runtime_resume = ata_port_runtime_resume, @@ -5525,18 +5512,17 @@ static const struct dev_pm_ops ata_port_pm_ops = { * level. sas suspend/resume is async to allow parallel port recovery * since sas has multiple ata_port instances per Scsi_Host. */ -int ata_sas_port_async_suspend(struct ata_port *ap, int *async) +void ata_sas_port_suspend(struct ata_port *ap) { - return __ata_port_suspend_common(ap, PMSG_SUSPEND, async); + ata_port_suspend_async(ap, PMSG_SUSPEND); } -EXPORT_SYMBOL_GPL(ata_sas_port_async_suspend); +EXPORT_SYMBOL_GPL(ata_sas_port_suspend); -int ata_sas_port_async_resume(struct ata_port *ap, int *async) +void ata_sas_port_resume(struct ata_port *ap) { - return __ata_port_resume_common(ap, PMSG_RESUME, async); + ata_port_resume_async(ap, PMSG_RESUME); } -EXPORT_SYMBOL_GPL(ata_sas_port_async_resume); - +EXPORT_SYMBOL_GPL(ata_sas_port_resume); /** * ata_host_suspend - suspend host diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 6d8757008318..6760fc4e85b8 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -95,12 +95,13 @@ enum { * represents timeout for that try. The first try can be soft or * hardreset. All others are hardreset if available. In most cases * the first reset w/ 10sec timeout should succeed. Following entries - * are mostly for error handling, hotplug and retarded devices. + * are mostly for error handling, hotplug and those outlier devices that + * take an exceptionally long time to recover from reset. */ static const unsigned long ata_eh_reset_timeouts[] = { 10000, /* most drives spin up by 10sec */ 10000, /* > 99% working drives spin up before 20sec */ - 35000, /* give > 30 secs of idleness for retarded devices */ + 35000, /* give > 30 secs of idleness for outlier devices */ 5000, /* and sweet one last chance */ ULONG_MAX, /* > 1 min has elapsed, give up */ }; @@ -4069,7 +4070,7 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) ata_acpi_set_state(ap, ap->pm_mesg); out: - /* report result */ + /* update the flags */ spin_lock_irqsave(ap->lock, flags); ap->pflags &= ~ATA_PFLAG_PM_PENDING; @@ -4078,11 +4079,6 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) else if (ap->pflags & ATA_PFLAG_FROZEN) ata_port_schedule_eh(ap); - if (ap->pm_result) { - *ap->pm_result = rc; - ap->pm_result = NULL; - } - spin_unlock_irqrestore(ap->lock, flags); return; @@ -4134,13 +4130,9 @@ static void ata_eh_handle_port_resume(struct ata_port *ap) /* tell ACPI that we're resuming */ ata_acpi_on_resume(ap); - /* report result */ + /* update the flags */ spin_lock_irqsave(ap->lock, flags); ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED); - if (ap->pm_result) { - *ap->pm_result = rc; - ap->pm_result = NULL; - } spin_unlock_irqrestore(ap->lock, flags); } #endif /* CONFIG_PM */ diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 88949c6d55dd..f3a65a3140d3 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -85,21 +85,6 @@ static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev) return ODD_MECH_TYPE_UNSUPPORTED; } -static bool odd_can_poweroff(struct ata_device *ata_dev) -{ - acpi_handle handle; - struct acpi_device *acpi_dev; - - handle = ata_dev_acpi_handle(ata_dev); - if (!handle) - return false; - - if (acpi_bus_get_device(handle, &acpi_dev)) - return false; - - return acpi_device_can_poweroff(acpi_dev); -} - /* Test if ODD is zero power ready by sense code */ static bool zpready(struct ata_device *dev) { @@ -267,13 +252,11 @@ static void ata_acpi_remove_pm_notifier(struct ata_device *dev) void zpodd_init(struct ata_device *dev) { + struct acpi_device *adev = ACPI_COMPANION(&dev->tdev); enum odd_mech_type mech_type; struct zpodd *zpodd; - if (dev->zpodd) - return; - - if (!odd_can_poweroff(dev)) + if (dev->zpodd || !adev || !acpi_device_can_poweroff(adev)) return; mech_type = zpodd_get_mech_type(dev); diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c index 62c9ac80c6e9..5108b8744dce 100644 --- a/drivers/ata/pata_acpi.c +++ b/drivers/ata/pata_acpi.c @@ -7,7 +7,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c index d23e2b3ca0b6..1206fa6b62ca 100644 --- a/drivers/ata/pata_amd.c +++ b/drivers/ata/pata_amd.c @@ -17,7 +17,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index 73492dd4a4bc..6fac524c2f50 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -356,7 +356,7 @@ static void cf_exit(struct arasan_cf_dev *acdev) static void dma_callback(void *dev) { - struct arasan_cf_dev *acdev = (struct arasan_cf_dev *) dev; + struct arasan_cf_dev *acdev = dev; complete(&acdev->dma_completion); } diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c index 1581dee2967a..3aa4e655e3c6 100644 --- a/drivers/ata/pata_artop.c +++ b/drivers/ata/pata_artop.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c index d63ee8f41a4f..e9c87274a781 100644 --- a/drivers/ata/pata_at91.c +++ b/drivers/ata/pata_at91.c @@ -18,7 +18,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/gfp.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c index 24e51056ac26..30fa4ca4cef6 100644 --- a/drivers/ata/pata_atiixp.c +++ b/drivers/ata/pata_atiixp.c @@ -15,7 +15,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_atp867x.c b/drivers/ata/pata_atp867x.c index 2ca5026f2c15..7e73a0f1e323 100644 --- a/drivers/ata/pata_atp867x.c +++ b/drivers/ata/pata_atp867x.c @@ -29,7 +29,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c index 8fb69e5ca1b7..57f1be64dbf2 100644 --- a/drivers/ata/pata_cmd640.c +++ b/drivers/ata/pata_cmd640.c @@ -15,7 +15,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/gfp.h> diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c index 1275a8d4dedc..6bca3505b9e9 100644 --- a/drivers/ata/pata_cmd64x.c +++ b/drivers/ata/pata_cmd64x.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c index f10baabbf5db..bcde4b786807 100644 --- a/drivers/ata/pata_cs5520.c +++ b/drivers/ata/pata_cs5520.c @@ -34,7 +34,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c index f07f2296acdc..8afe854a5a50 100644 --- a/drivers/ata/pata_cs5530.c +++ b/drivers/ata/pata_cs5530.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c index 997e16a3a63f..2c0986fa4bb2 100644 --- a/drivers/ata/pata_cs5535.c +++ b/drivers/ata/pata_cs5535.c @@ -31,7 +31,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c index 0448860a2077..32ddcae5a360 100644 --- a/drivers/ata/pata_cs5536.c +++ b/drivers/ata/pata_cs5536.c @@ -33,7 +33,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/libata.h> diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c index 810bc9964dde..3435bd6a5cc9 100644 --- a/drivers/ata/pata_cypress.c +++ b/drivers/ata/pata_cypress.c @@ -11,7 +11,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c index 3c12fd7acd41..f440892225f4 100644 --- a/drivers/ata/pata_efar.c +++ b/drivers/ata/pata_efar.c @@ -14,7 +14,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c index 980b88e109fc..cad9d45749c4 100644 --- a/drivers/ata/pata_ep93xx.c +++ b/drivers/ata/pata_ep93xx.c @@ -34,7 +34,6 @@ #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <scsi/scsi_host.h> #include <linux/ata.h> diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c index 35b521348d31..8e76f79689d3 100644 --- a/drivers/ata/pata_hpt366.c +++ b/drivers/ata/pata_hpt366.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c index a9d74eff5fc4..3ba843f5cdc0 100644 --- a/drivers/ata/pata_hpt37x.c +++ b/drivers/ata/pata_hpt37x.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c index 4be0398c153d..b93c0f0729e7 100644 --- a/drivers/ata/pata_hpt3x2n.c +++ b/drivers/ata/pata_hpt3x2n.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c index 85cf2861e0b7..255c5aaff3a8 100644 --- a/drivers/ata/pata_hpt3x3.c +++ b/drivers/ata/pata_hpt3x3.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c index b0b18ec5465f..e0872db913d6 100644 --- a/drivers/ata/pata_imx.c +++ b/drivers/ata/pata_imx.c @@ -15,7 +15,6 @@ */ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <scsi/scsi_host.h> #include <linux/ata.h> @@ -100,13 +99,9 @@ static int pata_imx_probe(struct platform_device *pdev) struct resource *io_res; int ret; - io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (io_res == NULL) - return -EINVAL; - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -EINVAL; + if (irq < 0) + return irq; priv = devm_kzalloc(&pdev->dev, sizeof(struct pata_imx_priv), GFP_KERNEL); @@ -136,11 +131,10 @@ static int pata_imx_probe(struct platform_device *pdev) ap->pio_mask = ATA_PIO0; ap->flags |= ATA_FLAG_SLAVE_POSS; - priv->host_regs = devm_ioremap(&pdev->dev, io_res->start, - resource_size(io_res)); - if (!priv->host_regs) { - dev_err(&pdev->dev, "failed to map IO/CTL base\n"); - ret = -EBUSY; + io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->host_regs = devm_ioremap_resource(&pdev->dev, io_res); + if (IS_ERR(priv->host_regs)) { + ret = PTR_ERR(priv->host_regs); goto err; } diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c index 2a8dd9527ecc..81369d187a5c 100644 --- a/drivers/ata/pata_it8213.c +++ b/drivers/ata/pata_it8213.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c index 581e04d80367..dc3d7877f29d 100644 --- a/drivers/ata/pata_it821x.c +++ b/drivers/ata/pata_it821x.c @@ -72,7 +72,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/slab.h> diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c index 76e739b031b6..b1cfa0258fd3 100644 --- a/drivers/ata/pata_jmicron.c +++ b/drivers/ata/pata_jmicron.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c index be816428b430..bce2a8ca4678 100644 --- a/drivers/ata/pata_legacy.c +++ b/drivers/ata/pata_legacy.c @@ -916,7 +916,6 @@ static __init int probe_chip_type(struct legacy_probe *probe) local_irq_restore(flags); return BIOS; } - local_irq_restore(flags); } if (ht6560a & mask) diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c index a4f5e781c8c2..6bad3df3a13c 100644 --- a/drivers/ata/pata_marvell.c +++ b/drivers/ata/pata_marvell.c @@ -11,7 +11,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c index 1f5f28bb0bb8..f39a5379e816 100644 --- a/drivers/ata/pata_mpiix.c +++ b/drivers/ata/pata_mpiix.c @@ -28,7 +28,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c index ad1a0febd620..e3b97093ef9a 100644 --- a/drivers/ata/pata_netcell.c +++ b/drivers/ata/pata_netcell.c @@ -7,7 +7,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_ninja32.c b/drivers/ata/pata_ninja32.c index 9513e071040d..56201a69af12 100644 --- a/drivers/ata/pata_ninja32.c +++ b/drivers/ata/pata_ninja32.c @@ -37,7 +37,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c index 0c424dae56e7..6154c3ee11a5 100644 --- a/drivers/ata/pata_ns87410.c +++ b/drivers/ata/pata_ns87410.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_ns87415.c b/drivers/ata/pata_ns87415.c index 16dc3a63a23d..d44df7ccfe43 100644 --- a/drivers/ata/pata_ns87415.c +++ b/drivers/ata/pata_ns87415.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c index d77b2e1054ef..319b64491b7b 100644 --- a/drivers/ata/pata_oldpiix.c +++ b/drivers/ata/pata_oldpiix.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c index 4ea70cd22aee..fb042e0519d0 100644 --- a/drivers/ata/pata_opti.c +++ b/drivers/ata/pata_opti.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c index 78ede3fd1875..bb71ea214b99 100644 --- a/drivers/ata/pata_optidma.c +++ b/drivers/ata/pata_optidma.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index 40254f4df584..bcc4b968c049 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/slab.h> diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c index 9d874c85d64d..1151f23177bb 100644 --- a/drivers/ata/pata_pdc2027x.c +++ b/drivers/ata/pata_pdc2027x.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c index c34fc50070a6..defa050e1784 100644 --- a/drivers/ata/pata_pdc202xx_old.c +++ b/drivers/ata/pata_pdc202xx_old.c @@ -15,7 +15,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_piccolo.c b/drivers/ata/pata_piccolo.c index 2beb6b5045f8..0b46be117051 100644 --- a/drivers/ata/pata_piccolo.c +++ b/drivers/ata/pata_piccolo.c @@ -18,7 +18,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index 02794885de10..a5579b55e332 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -13,7 +13,6 @@ */ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <scsi/scsi_host.h> #include <linux/ata.h> diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c index a6f05acad61e..73259bfda1e3 100644 --- a/drivers/ata/pata_pxa.c +++ b/drivers/ata/pata_pxa.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/ata.h> #include <linux/libata.h> diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c index f582ba180a7d..be3f10240dca 100644 --- a/drivers/ata/pata_radisys.c +++ b/drivers/ata/pata_radisys.c @@ -15,7 +15,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_rdc.c b/drivers/ata/pata_rdc.c index 79a970f05a2e..521b2137ea3e 100644 --- a/drivers/ata/pata_rdc.c +++ b/drivers/ata/pata_rdc.c @@ -24,7 +24,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c index 040b093617a4..caedc90855b2 100644 --- a/drivers/ata/pata_rz1000.c +++ b/drivers/ata/pata_rz1000.c @@ -14,7 +14,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c index ce2f828c17b3..96a232fffae6 100644 --- a/drivers/ata/pata_sc1200.c +++ b/drivers/ata/pata_sc1200.c @@ -32,7 +32,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c index f35f15f4d83e..f1f5b5ae3382 100644 --- a/drivers/ata/pata_scc.c +++ b/drivers/ata/pata_scc.c @@ -35,7 +35,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_sch.c b/drivers/ata/pata_sch.c index d3830c45a369..5a1cde0ea360 100644 --- a/drivers/ata/pata_sch.c +++ b/drivers/ata/pata_sch.c @@ -27,7 +27,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c index 96c6a79ef606..e27f31fe1b67 100644 --- a/drivers/ata/pata_serverworks.c +++ b/drivers/ata/pata_serverworks.c @@ -34,7 +34,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c index c4b0b073ba8e..73fe362d9716 100644 --- a/drivers/ata/pata_sil680.c +++ b/drivers/ata/pata_sil680.c @@ -25,7 +25,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c index 1e8363640bf5..78d913aa93c8 100644 --- a/drivers/ata/pata_sis.c +++ b/drivers/ata/pata_sis.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c index 6816911ac422..900f0e4a1faf 100644 --- a/drivers/ata/pata_sl82c105.c +++ b/drivers/ata/pata_sl82c105.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c index 94473da68c02..7bc78e264f9e 100644 --- a/drivers/ata/pata_triflex.c +++ b/drivers/ata/pata_triflex.c @@ -36,7 +36,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_host.h> diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index c3ab9a6c3965..f6c9632bdff6 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -55,7 +55,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/gfp.h> diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index 8ea6e6afd041..f10631beffa8 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c @@ -36,7 +36,6 @@ #include <linux/module.h> #include <linux/gfp.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 523524b68022..0bb2cabd2197 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -29,7 +29,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/device.h> #include <linux/of_address.h> #include <linux/of_irq.h> @@ -462,8 +461,7 @@ static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance) int chan; u32 tfr_reg, err_reg; unsigned long flags; - struct sata_dwc_device *hsdev = - (struct sata_dwc_device *)hsdev_instance; + struct sata_dwc_device *hsdev = hsdev_instance; struct ata_host *host = (struct ata_host *)hsdev->host; struct ata_port *ap; struct sata_dwc_device_port *hsdevp; diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c index 870b11eadc6d..65965cf5af06 100644 --- a/drivers/ata/sata_highbank.c +++ b/drivers/ata/sata_highbank.c @@ -19,7 +19,6 @@ #include <linux/kernel.h> #include <linux/gfp.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/types.h> #include <linux/err.h> #include <linux/io.h> @@ -142,7 +141,7 @@ static ssize_t ecx_transmit_led_message(struct ata_port *ap, u32 state, ssize_t size) { struct ahci_host_priv *hpriv = ap->host->private_data; - struct ecx_plat_data *pdata = (struct ecx_plat_data *) hpriv->plat_data; + struct ecx_plat_data *pdata = hpriv->plat_data; struct ahci_port_priv *pp = ap->private_data; unsigned long flags; int pmp, i; @@ -403,6 +402,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, static const unsigned long timing[] = { 5, 100, 500}; struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; bool online; @@ -431,7 +431,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, break; } while (!online && retry--); - ahci_start_engine(ap); + hpriv->start_engine(ap); if (online) *class = ahci_dev_classify(ap); diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index d74def823d3e..ba5f27120332 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -40,7 +40,6 @@ #include <linux/module.h> #include <linux/gfp.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 97f4acb54ad6..3638887476f6 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -35,7 +35,6 @@ #include <linux/module.h> #include <linux/gfp.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 3b0dd57984e1..9a6bd4cd29a0 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -31,7 +31,6 @@ #include <linux/module.h> #include <linux/gfp.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index b7695e804635..3062f8605b29 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -37,7 +37,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c index 1ad2f62d34b9..b513428171b3 100644 --- a/drivers/ata/sata_sis.c +++ b/drivers/ata/sata_sis.c @@ -33,7 +33,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index dc4f70179e7d..c630fa812624 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -39,7 +39,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index 9947010afc0f..39b5de60a1f9 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -82,7 +82,6 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -1021,8 +1020,7 @@ static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, idx++; dist = ((long) (window_size - (offset + size))) >= 0 ? size : (long) (window_size - offset); - memcpy_fromio((char *) psource, (char *) (dimm_mmio + offset / 4), - dist); + memcpy_fromio(psource, dimm_mmio + offset / 4, dist); psource += dist; size -= dist; @@ -1031,8 +1029,7 @@ static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, readl(mmio + PDC_GENERAL_CTLR); writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_fromio((char *) psource, (char *) (dimm_mmio), - window_size / 4); + memcpy_fromio(psource, dimm_mmio, window_size / 4); psource += window_size; size -= window_size; idx++; @@ -1043,8 +1040,7 @@ static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, readl(mmio + PDC_GENERAL_CTLR); writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_fromio((char *) psource, (char *) (dimm_mmio), - size / 4); + memcpy_fromio(psource, dimm_mmio, size / 4); } } #endif diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index 6d6489118873..08f98c3ed5c8 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -28,7 +28,6 @@ #include <linux/module.h> #include <linux/gfp.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index 87f056e54a9d..f72e84228c5c 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -36,7 +36,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index 44f304b3de63..29e847aac34b 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -37,7 +37,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 545c4de412c3..db4e264eecb6 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -791,6 +791,32 @@ void * devm_kmalloc(struct device *dev, size_t size, gfp_t gfp) EXPORT_SYMBOL_GPL(devm_kmalloc); /** + * devm_kstrdup - Allocate resource managed space and + * copy an existing string into that. + * @dev: Device to allocate memory for + * @s: the string to duplicate + * @gfp: the GFP mask used in the devm_kmalloc() call when + * allocating memory + * RETURNS: + * Pointer to allocated string on success, NULL on failure. + */ +char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp) +{ + size_t size; + char *buf; + + if (!s) + return NULL; + + size = strlen(s) + 1; + buf = devm_kmalloc(dev, size, gfp); + if (buf) + memcpy(buf, s, size); + return buf; +} +EXPORT_SYMBOL_GPL(devm_kstrdup); + +/** * devm_kfree - Resource-managed kfree * @dev: Device this memory belongs to * @p: Memory to free diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 2e58ebb1f6c0..1cb8544598d5 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,6 +1,5 @@ -obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o +obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o -obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 921b19234a4d..6f54962aae1d 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -41,7 +41,7 @@ struct gpd_timing_data *__td = &dev_gpd_data(dev)->td; \ if (!__retval && __elapsed > __td->field) { \ __td->field = __elapsed; \ - dev_warn(dev, name " latency exceeded, new value %lld ns\n", \ + dev_dbg(dev, name " latency exceeded, new value %lld ns\n", \ __elapsed); \ genpd->max_off_time_changed = true; \ __td->constraint_changed = true; \ diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 1b41fca3d65a..86d5e4fb5b98 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -29,6 +29,7 @@ #include <linux/async.h> #include <linux/suspend.h> #include <trace/events/power.h> +#include <linux/cpufreq.h> #include <linux/cpuidle.h> #include <linux/timer.h> @@ -91,6 +92,8 @@ void device_pm_sleep_init(struct device *dev) { dev->power.is_prepared = false; dev->power.is_suspended = false; + dev->power.is_noirq_suspended = false; + dev->power.is_late_suspended = false; init_completion(&dev->power.completion); complete_all(&dev->power.completion); dev->power.wakeup = NULL; @@ -467,7 +470,7 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd) * The driver of @dev will not receive interrupts while this function is being * executed. */ -static int device_resume_noirq(struct device *dev, pm_message_t state) +static int device_resume_noirq(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; char *info = NULL; @@ -479,6 +482,11 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) if (dev->power.syscore) goto Out; + if (!dev->power.is_noirq_suspended) + goto Out; + + dpm_wait(dev->parent, async); + if (dev->pm_domain) { info = "noirq power domain "; callback = pm_noirq_op(&dev->pm_domain->ops, state); @@ -499,12 +507,32 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) } error = dpm_run_callback(callback, dev, state, info); + dev->power.is_noirq_suspended = false; Out: + complete_all(&dev->power.completion); TRACE_RESUME(error); return error; } +static bool is_async(struct device *dev) +{ + return dev->power.async_suspend && pm_async_enabled + && !pm_trace_is_enabled(); +} + +static void async_resume_noirq(void *data, async_cookie_t cookie) +{ + struct device *dev = (struct device *)data; + int error; + + error = device_resume_noirq(dev, pm_transition, true); + if (error) + pm_dev_err(dev, pm_transition, " async", error); + + put_device(dev); +} + /** * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices. * @state: PM transition of the system being carried out. @@ -514,29 +542,48 @@ static int device_resume_noirq(struct device *dev, pm_message_t state) */ static void dpm_resume_noirq(pm_message_t state) { + struct device *dev; ktime_t starttime = ktime_get(); mutex_lock(&dpm_list_mtx); - while (!list_empty(&dpm_noirq_list)) { - struct device *dev = to_device(dpm_noirq_list.next); - int error; + pm_transition = state; + /* + * Advanced the async threads upfront, + * in case the starting of async threads is + * delayed by non-async resuming devices. + */ + list_for_each_entry(dev, &dpm_noirq_list, power.entry) { + reinit_completion(&dev->power.completion); + if (is_async(dev)) { + get_device(dev); + async_schedule(async_resume_noirq, dev); + } + } + + while (!list_empty(&dpm_noirq_list)) { + dev = to_device(dpm_noirq_list.next); get_device(dev); list_move_tail(&dev->power.entry, &dpm_late_early_list); mutex_unlock(&dpm_list_mtx); - error = device_resume_noirq(dev, state); - if (error) { - suspend_stats.failed_resume_noirq++; - dpm_save_failed_step(SUSPEND_RESUME_NOIRQ); - dpm_save_failed_dev(dev_name(dev)); - pm_dev_err(dev, state, " noirq", error); + if (!is_async(dev)) { + int error; + + error = device_resume_noirq(dev, state, false); + if (error) { + suspend_stats.failed_resume_noirq++; + dpm_save_failed_step(SUSPEND_RESUME_NOIRQ); + dpm_save_failed_dev(dev_name(dev)); + pm_dev_err(dev, state, " noirq", error); + } } mutex_lock(&dpm_list_mtx); put_device(dev); } mutex_unlock(&dpm_list_mtx); + async_synchronize_full(); dpm_show_time(starttime, state, "noirq"); resume_device_irqs(); cpuidle_resume(); @@ -549,7 +596,7 @@ static void dpm_resume_noirq(pm_message_t state) * * Runtime PM is disabled for @dev while this function is being executed. */ -static int device_resume_early(struct device *dev, pm_message_t state) +static int device_resume_early(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; char *info = NULL; @@ -561,6 +608,11 @@ static int device_resume_early(struct device *dev, pm_message_t state) if (dev->power.syscore) goto Out; + if (!dev->power.is_late_suspended) + goto Out; + + dpm_wait(dev->parent, async); + if (dev->pm_domain) { info = "early power domain "; callback = pm_late_early_op(&dev->pm_domain->ops, state); @@ -581,43 +633,75 @@ static int device_resume_early(struct device *dev, pm_message_t state) } error = dpm_run_callback(callback, dev, state, info); + dev->power.is_late_suspended = false; Out: TRACE_RESUME(error); pm_runtime_enable(dev); + complete_all(&dev->power.completion); return error; } +static void async_resume_early(void *data, async_cookie_t cookie) +{ + struct device *dev = (struct device *)data; + int error; + + error = device_resume_early(dev, pm_transition, true); + if (error) + pm_dev_err(dev, pm_transition, " async", error); + + put_device(dev); +} + /** * dpm_resume_early - Execute "early resume" callbacks for all devices. * @state: PM transition of the system being carried out. */ static void dpm_resume_early(pm_message_t state) { + struct device *dev; ktime_t starttime = ktime_get(); mutex_lock(&dpm_list_mtx); - while (!list_empty(&dpm_late_early_list)) { - struct device *dev = to_device(dpm_late_early_list.next); - int error; + pm_transition = state; + /* + * Advanced the async threads upfront, + * in case the starting of async threads is + * delayed by non-async resuming devices. + */ + list_for_each_entry(dev, &dpm_late_early_list, power.entry) { + reinit_completion(&dev->power.completion); + if (is_async(dev)) { + get_device(dev); + async_schedule(async_resume_early, dev); + } + } + + while (!list_empty(&dpm_late_early_list)) { + dev = to_device(dpm_late_early_list.next); get_device(dev); list_move_tail(&dev->power.entry, &dpm_suspended_list); mutex_unlock(&dpm_list_mtx); - error = device_resume_early(dev, state); - if (error) { - suspend_stats.failed_resume_early++; - dpm_save_failed_step(SUSPEND_RESUME_EARLY); - dpm_save_failed_dev(dev_name(dev)); - pm_dev_err(dev, state, " early", error); - } + if (!is_async(dev)) { + int error; + error = device_resume_early(dev, state, false); + if (error) { + suspend_stats.failed_resume_early++; + dpm_save_failed_step(SUSPEND_RESUME_EARLY); + dpm_save_failed_dev(dev_name(dev)); + pm_dev_err(dev, state, " early", error); + } + } mutex_lock(&dpm_list_mtx); put_device(dev); } mutex_unlock(&dpm_list_mtx); + async_synchronize_full(); dpm_show_time(starttime, state, "early"); } @@ -732,12 +816,6 @@ static void async_resume(void *data, async_cookie_t cookie) put_device(dev); } -static bool is_async(struct device *dev) -{ - return dev->power.async_suspend && pm_async_enabled - && !pm_trace_is_enabled(); -} - /** * dpm_resume - Execute "resume" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. @@ -789,6 +867,8 @@ void dpm_resume(pm_message_t state) mutex_unlock(&dpm_list_mtx); async_synchronize_full(); dpm_show_time(starttime, state, NULL); + + cpufreq_resume(); } /** @@ -913,13 +993,24 @@ static pm_message_t resume_event(pm_message_t sleep_state) * The driver of @dev will not receive interrupts while this function is being * executed. */ -static int device_suspend_noirq(struct device *dev, pm_message_t state) +static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; char *info = NULL; + int error = 0; + + if (async_error) + goto Complete; + + if (pm_wakeup_pending()) { + async_error = -EBUSY; + goto Complete; + } if (dev->power.syscore) - return 0; + goto Complete; + + dpm_wait_for_children(dev, async); if (dev->pm_domain) { info = "noirq power domain "; @@ -940,7 +1031,41 @@ static int device_suspend_noirq(struct device *dev, pm_message_t state) callback = pm_noirq_op(dev->driver->pm, state); } - return dpm_run_callback(callback, dev, state, info); + error = dpm_run_callback(callback, dev, state, info); + if (!error) + dev->power.is_noirq_suspended = true; + else + async_error = error; + +Complete: + complete_all(&dev->power.completion); + return error; +} + +static void async_suspend_noirq(void *data, async_cookie_t cookie) +{ + struct device *dev = (struct device *)data; + int error; + + error = __device_suspend_noirq(dev, pm_transition, true); + if (error) { + dpm_save_failed_dev(dev_name(dev)); + pm_dev_err(dev, pm_transition, " async", error); + } + + put_device(dev); +} + +static int device_suspend_noirq(struct device *dev) +{ + reinit_completion(&dev->power.completion); + + if (pm_async_enabled && dev->power.async_suspend) { + get_device(dev); + async_schedule(async_suspend_noirq, dev); + return 0; + } + return __device_suspend_noirq(dev, pm_transition, false); } /** @@ -958,19 +1083,20 @@ static int dpm_suspend_noirq(pm_message_t state) cpuidle_pause(); suspend_device_irqs(); mutex_lock(&dpm_list_mtx); + pm_transition = state; + async_error = 0; + while (!list_empty(&dpm_late_early_list)) { struct device *dev = to_device(dpm_late_early_list.prev); get_device(dev); mutex_unlock(&dpm_list_mtx); - error = device_suspend_noirq(dev, state); + error = device_suspend_noirq(dev); mutex_lock(&dpm_list_mtx); if (error) { pm_dev_err(dev, state, " noirq", error); - suspend_stats.failed_suspend_noirq++; - dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ); dpm_save_failed_dev(dev_name(dev)); put_device(dev); break; @@ -979,16 +1105,21 @@ static int dpm_suspend_noirq(pm_message_t state) list_move(&dev->power.entry, &dpm_noirq_list); put_device(dev); - if (pm_wakeup_pending()) { - error = -EBUSY; + if (async_error) break; - } } mutex_unlock(&dpm_list_mtx); - if (error) + async_synchronize_full(); + if (!error) + error = async_error; + + if (error) { + suspend_stats.failed_suspend_noirq++; + dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ); dpm_resume_noirq(resume_event(state)); - else + } else { dpm_show_time(starttime, state, "noirq"); + } return error; } @@ -999,15 +1130,26 @@ static int dpm_suspend_noirq(pm_message_t state) * * Runtime PM is disabled for @dev while this function is being executed. */ -static int device_suspend_late(struct device *dev, pm_message_t state) +static int __device_suspend_late(struct device *dev, pm_message_t state, bool async) { pm_callback_t callback = NULL; char *info = NULL; + int error = 0; __pm_runtime_disable(dev, false); + if (async_error) + goto Complete; + + if (pm_wakeup_pending()) { + async_error = -EBUSY; + goto Complete; + } + if (dev->power.syscore) - return 0; + goto Complete; + + dpm_wait_for_children(dev, async); if (dev->pm_domain) { info = "late power domain "; @@ -1028,7 +1170,41 @@ static int device_suspend_late(struct device *dev, pm_message_t state) callback = pm_late_early_op(dev->driver->pm, state); } - return dpm_run_callback(callback, dev, state, info); + error = dpm_run_callback(callback, dev, state, info); + if (!error) + dev->power.is_late_suspended = true; + else + async_error = error; + +Complete: + complete_all(&dev->power.completion); + return error; +} + +static void async_suspend_late(void *data, async_cookie_t cookie) +{ + struct device *dev = (struct device *)data; + int error; + + error = __device_suspend_late(dev, pm_transition, true); + if (error) { + dpm_save_failed_dev(dev_name(dev)); + pm_dev_err(dev, pm_transition, " async", error); + } + put_device(dev); +} + +static int device_suspend_late(struct device *dev) +{ + reinit_completion(&dev->power.completion); + + if (pm_async_enabled && dev->power.async_suspend) { + get_device(dev); + async_schedule(async_suspend_late, dev); + return 0; + } + + return __device_suspend_late(dev, pm_transition, false); } /** @@ -1041,19 +1217,20 @@ static int dpm_suspend_late(pm_message_t state) int error = 0; mutex_lock(&dpm_list_mtx); + pm_transition = state; + async_error = 0; + while (!list_empty(&dpm_suspended_list)) { struct device *dev = to_device(dpm_suspended_list.prev); get_device(dev); mutex_unlock(&dpm_list_mtx); - error = device_suspend_late(dev, state); + error = device_suspend_late(dev); mutex_lock(&dpm_list_mtx); if (error) { pm_dev_err(dev, state, " late", error); - suspend_stats.failed_suspend_late++; - dpm_save_failed_step(SUSPEND_SUSPEND_LATE); dpm_save_failed_dev(dev_name(dev)); put_device(dev); break; @@ -1062,17 +1239,18 @@ static int dpm_suspend_late(pm_message_t state) list_move(&dev->power.entry, &dpm_late_early_list); put_device(dev); - if (pm_wakeup_pending()) { - error = -EBUSY; + if (async_error) break; - } } mutex_unlock(&dpm_list_mtx); - if (error) + async_synchronize_full(); + if (error) { + suspend_stats.failed_suspend_late++; + dpm_save_failed_step(SUSPEND_SUSPEND_LATE); dpm_resume_early(resume_event(state)); - else + } else { dpm_show_time(starttime, state, "late"); - + } return error; } @@ -1259,6 +1437,8 @@ int dpm_suspend(pm_message_t state) might_sleep(); + cpufreq_suspend(); + mutex_lock(&dpm_list_mtx); pm_transition = state; async_error = 0; diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index cfc3226ec492..a21223d95926 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -89,8 +89,8 @@ extern void dpm_sysfs_remove(struct device *dev); extern void rpm_sysfs_remove(struct device *dev); extern int wakeup_sysfs_add(struct device *dev); extern void wakeup_sysfs_remove(struct device *dev); -extern int pm_qos_sysfs_add_latency(struct device *dev); -extern void pm_qos_sysfs_remove_latency(struct device *dev); +extern int pm_qos_sysfs_add_resume_latency(struct device *dev); +extern void pm_qos_sysfs_remove_resume_latency(struct device *dev); extern int pm_qos_sysfs_add_flags(struct device *dev); extern void pm_qos_sysfs_remove_flags(struct device *dev); diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 5c1361a9e5dd..36b9eb4862cb 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -105,7 +105,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_flags); s32 __dev_pm_qos_read_value(struct device *dev) { return IS_ERR_OR_NULL(dev->power.qos) ? - 0 : pm_qos_read_value(&dev->power.qos->latency); + 0 : pm_qos_read_value(&dev->power.qos->resume_latency); } /** @@ -141,16 +141,24 @@ static int apply_constraint(struct dev_pm_qos_request *req, int ret; switch(req->type) { - case DEV_PM_QOS_LATENCY: - ret = pm_qos_update_target(&qos->latency, &req->data.pnode, - action, value); + case DEV_PM_QOS_RESUME_LATENCY: + ret = pm_qos_update_target(&qos->resume_latency, + &req->data.pnode, action, value); if (ret) { - value = pm_qos_read_value(&qos->latency); + value = pm_qos_read_value(&qos->resume_latency); blocking_notifier_call_chain(&dev_pm_notifiers, (unsigned long)value, req); } break; + case DEV_PM_QOS_LATENCY_TOLERANCE: + ret = pm_qos_update_target(&qos->latency_tolerance, + &req->data.pnode, action, value); + if (ret) { + value = pm_qos_read_value(&qos->latency_tolerance); + req->dev->power.set_latency_tolerance(req->dev, value); + } + break; case DEV_PM_QOS_FLAGS: ret = pm_qos_update_flags(&qos->flags, &req->data.flr, action, value); @@ -186,13 +194,21 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) } BLOCKING_INIT_NOTIFIER_HEAD(n); - c = &qos->latency; + c = &qos->resume_latency; plist_head_init(&c->list); - c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; - c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; + c->target_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; + c->default_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; + c->no_constraint_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; c->type = PM_QOS_MIN; c->notifiers = n; + c = &qos->latency_tolerance; + plist_head_init(&c->list); + c->target_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE; + c->default_value = PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE; + c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; + c->type = PM_QOS_MIN; + INIT_LIST_HEAD(&qos->flags.list); spin_lock_irq(&dev->power.lock); @@ -224,7 +240,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) * If the device's PM QoS resume latency limit or PM QoS flags have been * exposed to user space, they have to be hidden at this point. */ - pm_qos_sysfs_remove_latency(dev); + pm_qos_sysfs_remove_resume_latency(dev); pm_qos_sysfs_remove_flags(dev); mutex_lock(&dev_pm_qos_mtx); @@ -237,7 +253,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) goto out; /* Flush the constraints lists for the device. */ - c = &qos->latency; + c = &qos->resume_latency; plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { /* * Update constraints list and call the notification @@ -246,6 +262,11 @@ void dev_pm_qos_constraints_destroy(struct device *dev) apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); } + c = &qos->latency_tolerance; + plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { + apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); + memset(req, 0, sizeof(*req)); + } f = &qos->flags; list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); @@ -265,6 +286,40 @@ void dev_pm_qos_constraints_destroy(struct device *dev) mutex_unlock(&dev_pm_qos_sysfs_mtx); } +static bool dev_pm_qos_invalid_request(struct device *dev, + struct dev_pm_qos_request *req) +{ + return !req || (req->type == DEV_PM_QOS_LATENCY_TOLERANCE + && !dev->power.set_latency_tolerance); +} + +static int __dev_pm_qos_add_request(struct device *dev, + struct dev_pm_qos_request *req, + enum dev_pm_qos_req_type type, s32 value) +{ + int ret = 0; + + if (!dev || dev_pm_qos_invalid_request(dev, req)) + return -EINVAL; + + if (WARN(dev_pm_qos_request_active(req), + "%s() called for already added request\n", __func__)) + return -EINVAL; + + if (IS_ERR(dev->power.qos)) + ret = -ENODEV; + else if (!dev->power.qos) + ret = dev_pm_qos_constraints_allocate(dev); + + trace_dev_pm_qos_add_request(dev_name(dev), type, value); + if (!ret) { + req->dev = dev; + req->type = type; + ret = apply_constraint(req, PM_QOS_ADD_REQ, value); + } + return ret; +} + /** * dev_pm_qos_add_request - inserts new qos request into the list * @dev: target device for the constraint @@ -290,31 +345,11 @@ void dev_pm_qos_constraints_destroy(struct device *dev) int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, enum dev_pm_qos_req_type type, s32 value) { - int ret = 0; - - if (!dev || !req) /*guard against callers passing in null */ - return -EINVAL; - - if (WARN(dev_pm_qos_request_active(req), - "%s() called for already added request\n", __func__)) - return -EINVAL; + int ret; mutex_lock(&dev_pm_qos_mtx); - - if (IS_ERR(dev->power.qos)) - ret = -ENODEV; - else if (!dev->power.qos) - ret = dev_pm_qos_constraints_allocate(dev); - - trace_dev_pm_qos_add_request(dev_name(dev), type, value); - if (!ret) { - req->dev = dev; - req->type = type; - ret = apply_constraint(req, PM_QOS_ADD_REQ, value); - } - + ret = __dev_pm_qos_add_request(dev, req, type, value); mutex_unlock(&dev_pm_qos_mtx); - return ret; } EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); @@ -341,7 +376,8 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, return -ENODEV; switch(req->type) { - case DEV_PM_QOS_LATENCY: + case DEV_PM_QOS_RESUME_LATENCY: + case DEV_PM_QOS_LATENCY_TOLERANCE: curr_value = req->data.pnode.prio; break; case DEV_PM_QOS_FLAGS: @@ -460,8 +496,8 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) ret = dev_pm_qos_constraints_allocate(dev); if (!ret) - ret = blocking_notifier_chain_register( - dev->power.qos->latency.notifiers, notifier); + ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers, + notifier); mutex_unlock(&dev_pm_qos_mtx); return ret; @@ -487,9 +523,8 @@ int dev_pm_qos_remove_notifier(struct device *dev, /* Silently return if the constraints object is not present. */ if (!IS_ERR_OR_NULL(dev->power.qos)) - retval = blocking_notifier_chain_unregister( - dev->power.qos->latency.notifiers, - notifier); + retval = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers, + notifier); mutex_unlock(&dev_pm_qos_mtx); return retval; @@ -530,20 +565,32 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier); * dev_pm_qos_add_ancestor_request - Add PM QoS request for device's ancestor. * @dev: Device whose ancestor to add the request for. * @req: Pointer to the preallocated handle. + * @type: Type of the request. * @value: Constraint latency value. */ int dev_pm_qos_add_ancestor_request(struct device *dev, - struct dev_pm_qos_request *req, s32 value) + struct dev_pm_qos_request *req, + enum dev_pm_qos_req_type type, s32 value) { struct device *ancestor = dev->parent; int ret = -ENODEV; - while (ancestor && !ancestor->power.ignore_children) - ancestor = ancestor->parent; + switch (type) { + case DEV_PM_QOS_RESUME_LATENCY: + while (ancestor && !ancestor->power.ignore_children) + ancestor = ancestor->parent; + break; + case DEV_PM_QOS_LATENCY_TOLERANCE: + while (ancestor && !ancestor->power.set_latency_tolerance) + ancestor = ancestor->parent; + + break; + default: + ancestor = NULL; + } if (ancestor) - ret = dev_pm_qos_add_request(ancestor, req, - DEV_PM_QOS_LATENCY, value); + ret = dev_pm_qos_add_request(ancestor, req, type, value); if (ret < 0) req->dev = NULL; @@ -559,9 +606,13 @@ static void __dev_pm_qos_drop_user_request(struct device *dev, struct dev_pm_qos_request *req = NULL; switch(type) { - case DEV_PM_QOS_LATENCY: - req = dev->power.qos->latency_req; - dev->power.qos->latency_req = NULL; + case DEV_PM_QOS_RESUME_LATENCY: + req = dev->power.qos->resume_latency_req; + dev->power.qos->resume_latency_req = NULL; + break; + case DEV_PM_QOS_LATENCY_TOLERANCE: + req = dev->power.qos->latency_tolerance_req; + dev->power.qos->latency_tolerance_req = NULL; break; case DEV_PM_QOS_FLAGS: req = dev->power.qos->flags_req; @@ -597,7 +648,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (!req) return -ENOMEM; - ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY, value); + ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_RESUME_LATENCY, value); if (ret < 0) { kfree(req); return ret; @@ -609,7 +660,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (IS_ERR_OR_NULL(dev->power.qos)) ret = -ENODEV; - else if (dev->power.qos->latency_req) + else if (dev->power.qos->resume_latency_req) ret = -EEXIST; if (ret < 0) { @@ -618,13 +669,13 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) mutex_unlock(&dev_pm_qos_mtx); goto out; } - dev->power.qos->latency_req = req; + dev->power.qos->resume_latency_req = req; mutex_unlock(&dev_pm_qos_mtx); - ret = pm_qos_sysfs_add_latency(dev); + ret = pm_qos_sysfs_add_resume_latency(dev); if (ret) - dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); + dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_RESUME_LATENCY); out: mutex_unlock(&dev_pm_qos_sysfs_mtx); @@ -634,8 +685,8 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); static void __dev_pm_qos_hide_latency_limit(struct device *dev) { - if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->latency_req) - __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); + if (!IS_ERR_OR_NULL(dev->power.qos) && dev->power.qos->resume_latency_req) + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_RESUME_LATENCY); } /** @@ -646,7 +697,7 @@ void dev_pm_qos_hide_latency_limit(struct device *dev) { mutex_lock(&dev_pm_qos_sysfs_mtx); - pm_qos_sysfs_remove_latency(dev); + pm_qos_sysfs_remove_resume_latency(dev); mutex_lock(&dev_pm_qos_mtx); __dev_pm_qos_hide_latency_limit(dev); @@ -768,6 +819,67 @@ int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) pm_runtime_put(dev); return ret; } + +/** + * dev_pm_qos_get_user_latency_tolerance - Get user space latency tolerance. + * @dev: Device to obtain the user space latency tolerance for. + */ +s32 dev_pm_qos_get_user_latency_tolerance(struct device *dev) +{ + s32 ret; + + mutex_lock(&dev_pm_qos_mtx); + ret = IS_ERR_OR_NULL(dev->power.qos) + || !dev->power.qos->latency_tolerance_req ? + PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT : + dev->power.qos->latency_tolerance_req->data.pnode.prio; + mutex_unlock(&dev_pm_qos_mtx); + return ret; +} + +/** + * dev_pm_qos_update_user_latency_tolerance - Update user space latency tolerance. + * @dev: Device to update the user space latency tolerance for. + * @val: New user space latency tolerance for @dev (negative values disable). + */ +int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val) +{ + int ret; + + mutex_lock(&dev_pm_qos_mtx); + + if (IS_ERR_OR_NULL(dev->power.qos) + || !dev->power.qos->latency_tolerance_req) { + struct dev_pm_qos_request *req; + + if (val < 0) { + ret = -EINVAL; + goto out; + } + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto out; + } + ret = __dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY_TOLERANCE, val); + if (ret < 0) { + kfree(req); + goto out; + } + dev->power.qos->latency_tolerance_req = req; + } else { + if (val < 0) { + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY_TOLERANCE); + ret = 0; + } else { + ret = __dev_pm_qos_update_request(dev->power.qos->latency_tolerance_req, val); + } + } + + out: + mutex_unlock(&dev_pm_qos_mtx); + return ret; +} #else /* !CONFIG_PM_RUNTIME */ static void __dev_pm_qos_hide_latency_limit(struct device *dev) {} static void __dev_pm_qos_hide_flags(struct device *dev) {} diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 72e00e66ecc5..67c7938e430b 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -13,6 +13,43 @@ #include <trace/events/rpm.h> #include "power.h" +#define RPM_GET_CALLBACK(dev, cb) \ +({ \ + int (*__rpm_cb)(struct device *__d); \ + \ + if (dev->pm_domain) \ + __rpm_cb = dev->pm_domain->ops.cb; \ + else if (dev->type && dev->type->pm) \ + __rpm_cb = dev->type->pm->cb; \ + else if (dev->class && dev->class->pm) \ + __rpm_cb = dev->class->pm->cb; \ + else if (dev->bus && dev->bus->pm) \ + __rpm_cb = dev->bus->pm->cb; \ + else \ + __rpm_cb = NULL; \ + \ + if (!__rpm_cb && dev->driver && dev->driver->pm) \ + __rpm_cb = dev->driver->pm->cb; \ + \ + __rpm_cb; \ +}) + +static int (*rpm_get_suspend_cb(struct device *dev))(struct device *) +{ + return RPM_GET_CALLBACK(dev, runtime_suspend); +} + +static int (*rpm_get_resume_cb(struct device *dev))(struct device *) +{ + return RPM_GET_CALLBACK(dev, runtime_resume); +} + +#ifdef CONFIG_PM_RUNTIME +static int (*rpm_get_idle_cb(struct device *dev))(struct device *) +{ + return RPM_GET_CALLBACK(dev, runtime_idle); +} + static int rpm_resume(struct device *dev, int rpmflags); static int rpm_suspend(struct device *dev, int rpmflags); @@ -310,19 +347,7 @@ static int rpm_idle(struct device *dev, int rpmflags) dev->power.idle_notification = true; - if (dev->pm_domain) - callback = dev->pm_domain->ops.runtime_idle; - else if (dev->type && dev->type->pm) - callback = dev->type->pm->runtime_idle; - else if (dev->class && dev->class->pm) - callback = dev->class->pm->runtime_idle; - else if (dev->bus && dev->bus->pm) - callback = dev->bus->pm->runtime_idle; - else - callback = NULL; - - if (!callback && dev->driver && dev->driver->pm) - callback = dev->driver->pm->runtime_idle; + callback = rpm_get_idle_cb(dev); if (callback) retval = __rpm_callback(callback, dev); @@ -492,19 +517,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) __update_runtime_status(dev, RPM_SUSPENDING); - if (dev->pm_domain) - callback = dev->pm_domain->ops.runtime_suspend; - else if (dev->type && dev->type->pm) - callback = dev->type->pm->runtime_suspend; - else if (dev->class && dev->class->pm) - callback = dev->class->pm->runtime_suspend; - else if (dev->bus && dev->bus->pm) - callback = dev->bus->pm->runtime_suspend; - else - callback = NULL; - - if (!callback && dev->driver && dev->driver->pm) - callback = dev->driver->pm->runtime_suspend; + callback = rpm_get_suspend_cb(dev); retval = rpm_callback(callback, dev); if (retval) @@ -724,19 +737,7 @@ static int rpm_resume(struct device *dev, int rpmflags) __update_runtime_status(dev, RPM_RESUMING); - if (dev->pm_domain) - callback = dev->pm_domain->ops.runtime_resume; - else if (dev->type && dev->type->pm) - callback = dev->type->pm->runtime_resume; - else if (dev->class && dev->class->pm) - callback = dev->class->pm->runtime_resume; - else if (dev->bus && dev->bus->pm) - callback = dev->bus->pm->runtime_resume; - else - callback = NULL; - - if (!callback && dev->driver && dev->driver->pm) - callback = dev->driver->pm->runtime_resume; + callback = rpm_get_resume_cb(dev); retval = rpm_callback(callback, dev); if (retval) { @@ -1130,7 +1131,7 @@ EXPORT_SYMBOL_GPL(pm_runtime_barrier); * @dev: Device to handle. * @check_resume: If set, check if there's a resume request for the device. * - * Increment power.disable_depth for the device and if was zero previously, + * Increment power.disable_depth for the device and if it was zero previously, * cancel all pending runtime PM requests for the device and wait for all * operations in progress to complete. The device can be either active or * suspended after its runtime PM has been disabled. @@ -1401,3 +1402,86 @@ void pm_runtime_remove(struct device *dev) if (dev->power.irq_safe && dev->parent) pm_runtime_put(dev->parent); } +#endif + +/** + * pm_runtime_force_suspend - Force a device into suspend state if needed. + * @dev: Device to suspend. + * + * Disable runtime PM so we safely can check the device's runtime PM status and + * if it is active, invoke it's .runtime_suspend callback to bring it into + * suspend state. Keep runtime PM disabled to preserve the state unless we + * encounter errors. + * + * Typically this function may be invoked from a system suspend callback to make + * sure the device is put into low power state. + */ +int pm_runtime_force_suspend(struct device *dev) +{ + int (*callback)(struct device *); + int ret = 0; + + pm_runtime_disable(dev); + + /* + * Note that pm_runtime_status_suspended() returns false while + * !CONFIG_PM_RUNTIME, which means the device will be put into low + * power state. + */ + if (pm_runtime_status_suspended(dev)) + return 0; + + callback = rpm_get_suspend_cb(dev); + + if (!callback) { + ret = -ENOSYS; + goto err; + } + + ret = callback(dev); + if (ret) + goto err; + + pm_runtime_set_suspended(dev); + return 0; +err: + pm_runtime_enable(dev); + return ret; +} +EXPORT_SYMBOL_GPL(pm_runtime_force_suspend); + +/** + * pm_runtime_force_resume - Force a device into resume state. + * @dev: Device to resume. + * + * Prior invoking this function we expect the user to have brought the device + * into low power state by a call to pm_runtime_force_suspend(). Here we reverse + * those actions and brings the device into full power. We update the runtime PM + * status and re-enables runtime PM. + * + * Typically this function may be invoked from a system resume callback to make + * sure the device is put into full power state. + */ +int pm_runtime_force_resume(struct device *dev) +{ + int (*callback)(struct device *); + int ret = 0; + + callback = rpm_get_resume_cb(dev); + + if (!callback) { + ret = -ENOSYS; + goto out; + } + + ret = callback(dev); + if (ret) + goto out; + + pm_runtime_set_active(dev); + pm_runtime_mark_last_busy(dev); +out: + pm_runtime_enable(dev); + return ret; +} +EXPORT_SYMBOL_GPL(pm_runtime_force_resume); diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 03e089ade5ce..95b181d1ca6d 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -218,15 +218,16 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev, static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, autosuspend_delay_ms_store); -static ssize_t pm_qos_latency_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t pm_qos_resume_latency_show(struct device *dev, + struct device_attribute *attr, + char *buf) { - return sprintf(buf, "%d\n", dev_pm_qos_requested_latency(dev)); + return sprintf(buf, "%d\n", dev_pm_qos_requested_resume_latency(dev)); } -static ssize_t pm_qos_latency_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t n) +static ssize_t pm_qos_resume_latency_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) { s32 value; int ret; @@ -237,12 +238,47 @@ static ssize_t pm_qos_latency_store(struct device *dev, if (value < 0) return -EINVAL; - ret = dev_pm_qos_update_request(dev->power.qos->latency_req, value); + ret = dev_pm_qos_update_request(dev->power.qos->resume_latency_req, + value); return ret < 0 ? ret : n; } static DEVICE_ATTR(pm_qos_resume_latency_us, 0644, - pm_qos_latency_show, pm_qos_latency_store); + pm_qos_resume_latency_show, pm_qos_resume_latency_store); + +static ssize_t pm_qos_latency_tolerance_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + s32 value = dev_pm_qos_get_user_latency_tolerance(dev); + + if (value < 0) + return sprintf(buf, "auto\n"); + else if (value == PM_QOS_LATENCY_ANY) + return sprintf(buf, "any\n"); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t pm_qos_latency_tolerance_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + s32 value; + int ret; + + if (kstrtos32(buf, 0, &value)) { + if (!strcmp(buf, "auto") || !strcmp(buf, "auto\n")) + value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; + else if (!strcmp(buf, "any") || !strcmp(buf, "any\n")) + value = PM_QOS_LATENCY_ANY; + } + ret = dev_pm_qos_update_user_latency_tolerance(dev, value); + return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_latency_tolerance_us, 0644, + pm_qos_latency_tolerance_show, pm_qos_latency_tolerance_store); static ssize_t pm_qos_no_power_off_show(struct device *dev, struct device_attribute *attr, @@ -618,15 +654,26 @@ static struct attribute_group pm_runtime_attr_group = { .attrs = runtime_attrs, }; -static struct attribute *pm_qos_latency_attrs[] = { +static struct attribute *pm_qos_resume_latency_attrs[] = { #ifdef CONFIG_PM_RUNTIME &dev_attr_pm_qos_resume_latency_us.attr, #endif /* CONFIG_PM_RUNTIME */ NULL, }; -static struct attribute_group pm_qos_latency_attr_group = { +static struct attribute_group pm_qos_resume_latency_attr_group = { + .name = power_group_name, + .attrs = pm_qos_resume_latency_attrs, +}; + +static struct attribute *pm_qos_latency_tolerance_attrs[] = { +#ifdef CONFIG_PM_RUNTIME + &dev_attr_pm_qos_latency_tolerance_us.attr, +#endif /* CONFIG_PM_RUNTIME */ + NULL, +}; +static struct attribute_group pm_qos_latency_tolerance_attr_group = { .name = power_group_name, - .attrs = pm_qos_latency_attrs, + .attrs = pm_qos_latency_tolerance_attrs, }; static struct attribute *pm_qos_flags_attrs[] = { @@ -654,18 +701,23 @@ int dpm_sysfs_add(struct device *dev) if (rc) goto err_out; } - if (device_can_wakeup(dev)) { rc = sysfs_merge_group(&dev->kobj, &pm_wakeup_attr_group); - if (rc) { - if (pm_runtime_callbacks_present(dev)) - sysfs_unmerge_group(&dev->kobj, - &pm_runtime_attr_group); - goto err_out; - } + if (rc) + goto err_runtime; + } + if (dev->power.set_latency_tolerance) { + rc = sysfs_merge_group(&dev->kobj, + &pm_qos_latency_tolerance_attr_group); + if (rc) + goto err_wakeup; } return 0; + err_wakeup: + sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); + err_runtime: + sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group); err_out: sysfs_remove_group(&dev->kobj, &pm_attr_group); return rc; @@ -681,14 +733,14 @@ void wakeup_sysfs_remove(struct device *dev) sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); } -int pm_qos_sysfs_add_latency(struct device *dev) +int pm_qos_sysfs_add_resume_latency(struct device *dev) { - return sysfs_merge_group(&dev->kobj, &pm_qos_latency_attr_group); + return sysfs_merge_group(&dev->kobj, &pm_qos_resume_latency_attr_group); } -void pm_qos_sysfs_remove_latency(struct device *dev) +void pm_qos_sysfs_remove_resume_latency(struct device *dev) { - sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_attr_group); + sysfs_unmerge_group(&dev->kobj, &pm_qos_resume_latency_attr_group); } int pm_qos_sysfs_add_flags(struct device *dev) @@ -708,6 +760,7 @@ void rpm_sysfs_remove(struct device *dev) void dpm_sysfs_remove(struct device *dev) { + sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group); dev_pm_qos_constraints_destroy(dev); rpm_sysfs_remove(dev); sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 33414b1de201..7d1326985bee 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -134,6 +134,8 @@ struct regmap { /* if set, converts bulk rw to single rw */ bool use_single_rw; + /* if set, the device supports multi write mode */ + bool can_multi_write; struct rb_root range_tree; void *selector_work_buf; /* Scratch buffer used for selector */ diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index d4dd77134814..29b4128da0b0 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -249,11 +249,12 @@ static int regcache_default_sync(struct regmap *map, unsigned int min, { unsigned int reg; - for (reg = min; reg <= max; reg++) { + for (reg = min; reg <= max; reg += map->reg_stride) { unsigned int val; int ret; - if (regmap_volatile(map, reg)) + if (regmap_volatile(map, reg) || + !regmap_writeable(map, reg)) continue; ret = regcache_read(map, reg, &val); @@ -312,10 +313,6 @@ int regcache_sync(struct regmap *map) /* Apply any patch first */ map->cache_bypass = 1; for (i = 0; i < map->patch_regs; i++) { - if (map->patch[i].reg % map->reg_stride) { - ret = -EINVAL; - goto out; - } ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def); if (ret != 0) { dev_err(map->dev, "Failed to write %x = %x: %d\n", @@ -636,10 +633,10 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, if (*data == NULL) return 0; - count = cur - base; + count = (cur - base) / map->reg_stride; dev_dbg(map->dev, "Writing %zu bytes for %d registers from 0x%x-0x%x\n", - count * val_bytes, count, base, cur - 1); + count * val_bytes, count, base, cur - map->reg_stride); map->cache_bypass = 1; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index c5471cd6ebb7..45d812c0ea77 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -511,7 +511,7 @@ void regmap_debugfs_init(struct regmap *map, const char *name) debugfs_create_file("range", 0400, map->debugfs, map, ®map_reg_ranges_fops); - if (map->max_register) { + if (map->max_register || regmap_readable(map, 0)) { debugfs_create_file("registers", 0400, map->debugfs, map, ®map_map_fops); debugfs_create_file("access", 0400, map->debugfs, diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 82692068d3cb..edf88f20cbce 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -368,8 +368,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d) return -ENOMEM; - *data = d; - d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, GFP_KERNEL); if (!d->status_buf) @@ -506,6 +504,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, goto err_domain; } + *data = d; + return 0; err_domain: @@ -533,7 +533,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) return; free_irq(irq, d); - /* We should unmap the domain but... */ + irq_domain_remove(d->domain); kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 4410cb2d7d82..1e03e7f8bacb 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -25,10 +25,47 @@ struct regmap_mmio_context { void __iomem *regs; + unsigned reg_bytes; unsigned val_bytes; + unsigned pad_bytes; struct clk *clk; }; +static inline void regmap_mmio_regsize_check(size_t reg_size) +{ + switch (reg_size) { + case 1: + case 2: + case 4: +#ifdef CONFIG_64BIT + case 8: +#endif + break; + default: + BUG(); + } +} + +static int regmap_mmio_regbits_check(size_t reg_bits) +{ + switch (reg_bits) { + case 8: + case 16: + case 32: +#ifdef CONFIG_64BIT + case 64: +#endif + return 0; + default: + return -EINVAL; + } +} + +static inline void regmap_mmio_count_check(size_t count) +{ + BUG_ON(count % 2 != 0); +} + static int regmap_mmio_gather_write(void *context, const void *reg, size_t reg_size, const void *val, size_t val_size) @@ -37,7 +74,7 @@ static int regmap_mmio_gather_write(void *context, u32 offset; int ret; - BUG_ON(reg_size != 4); + regmap_mmio_regsize_check(reg_size); if (!IS_ERR(ctx->clk)) { ret = clk_enable(ctx->clk); @@ -80,9 +117,13 @@ static int regmap_mmio_gather_write(void *context, static int regmap_mmio_write(void *context, const void *data, size_t count) { - BUG_ON(count < 4); + struct regmap_mmio_context *ctx = context; + u32 offset = ctx->reg_bytes + ctx->pad_bytes; + + regmap_mmio_count_check(count); - return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4); + return regmap_mmio_gather_write(context, data, ctx->reg_bytes, + data + offset, count - offset); } static int regmap_mmio_read(void *context, @@ -93,7 +134,7 @@ static int regmap_mmio_read(void *context, u32 offset; int ret; - BUG_ON(reg_size != 4); + regmap_mmio_regsize_check(reg_size); if (!IS_ERR(ctx->clk)) { ret = clk_enable(ctx->clk); @@ -164,8 +205,9 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, int min_stride; int ret; - if (config->reg_bits != 32) - return ERR_PTR(-EINVAL); + ret = regmap_mmio_regbits_check(config->reg_bits); + if (ret) + return ERR_PTR(ret); if (config->pad_bits) return ERR_PTR(-EINVAL); @@ -208,6 +250,8 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, ctx->regs = regs; ctx->val_bytes = config->val_bits / 8; + ctx->reg_bytes = config->reg_bits / 8; + ctx->pad_bytes = config->pad_bits / 8; ctx->clk = ERR_PTR(-ENODEV); if (clk_id == NULL) diff --git a/drivers/base/regmap/regmap-spmi.c b/drivers/base/regmap/regmap-spmi.c index ac2391013db1..d7026dc33388 100644 --- a/drivers/base/regmap/regmap-spmi.c +++ b/drivers/base/regmap/regmap-spmi.c @@ -22,69 +22,235 @@ #include <linux/module.h> #include <linux/init.h> -static int regmap_spmi_read(void *context, - const void *reg, size_t reg_size, - void *val, size_t val_size) +static int regmap_spmi_base_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) { + u8 addr = *(u8 *)reg; + int err = 0; + + BUG_ON(reg_size != 1); + + while (val_size-- && !err) + err = spmi_register_read(context, addr++, val++); + + return err; +} + +static int regmap_spmi_base_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + const u8 *data = val; + u8 addr = *(u8 *)reg; + int err = 0; + + BUG_ON(reg_size != 1); + + /* + * SPMI defines a more bandwidth-efficient 'Register 0 Write' sequence, + * use it when possible. + */ + if (addr == 0 && val_size) { + err = spmi_register_zero_write(context, *data); + if (err) + goto err_out; + + data++; + addr++; + val_size--; + } + + while (val_size) { + err = spmi_register_write(context, addr, *data); + if (err) + goto err_out; + + data++; + addr++; + val_size--; + } + +err_out: + return err; +} + +static int regmap_spmi_base_write(void *context, const void *data, + size_t count) +{ + BUG_ON(count < 1); + return regmap_spmi_base_gather_write(context, data, 1, data + 1, + count - 1); +} + +static struct regmap_bus regmap_spmi_base = { + .read = regmap_spmi_base_read, + .write = regmap_spmi_base_write, + .gather_write = regmap_spmi_base_gather_write, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +/** + * regmap_init_spmi_base(): Create regmap for the Base register space + * @sdev: SPMI device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_spmi_base(struct spmi_device *sdev, + const struct regmap_config *config) +{ + return regmap_init(&sdev->dev, ®map_spmi_base, sdev, config); +} +EXPORT_SYMBOL_GPL(regmap_init_spmi_base); + +/** + * devm_regmap_init_spmi_base(): Create managed regmap for Base register space + * @sdev: SPMI device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_spmi_base(struct spmi_device *sdev, + const struct regmap_config *config) +{ + return devm_regmap_init(&sdev->dev, ®map_spmi_base, sdev, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_base); + +static int regmap_spmi_ext_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + int err = 0; + size_t len; + u16 addr; + BUG_ON(reg_size != 2); - return spmi_ext_register_readl(context, *(u16 *)reg, - val, val_size); + + addr = *(u16 *)reg; + + /* + * Split accesses into two to take advantage of the more + * bandwidth-efficient 'Extended Register Read' command when possible + */ + while (addr <= 0xFF && val_size) { + len = min_t(size_t, val_size, 16); + + err = spmi_ext_register_read(context, addr, val, len); + if (err) + goto err_out; + + addr += len; + val += len; + val_size -= len; + } + + while (val_size) { + len = min_t(size_t, val_size, 8); + + err = spmi_ext_register_readl(context, addr, val, val_size); + if (err) + goto err_out; + + addr += len; + val += len; + val_size -= len; + } + +err_out: + return err; } -static int regmap_spmi_gather_write(void *context, - const void *reg, size_t reg_size, - const void *val, size_t val_size) +static int regmap_spmi_ext_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) { + int err = 0; + size_t len; + u16 addr; + BUG_ON(reg_size != 2); - return spmi_ext_register_writel(context, *(u16 *)reg, val, val_size); + + addr = *(u16 *)reg; + + while (addr <= 0xFF && val_size) { + len = min_t(size_t, val_size, 16); + + err = spmi_ext_register_write(context, addr, val, len); + if (err) + goto err_out; + + addr += len; + val += len; + val_size -= len; + } + + while (val_size) { + len = min_t(size_t, val_size, 8); + + err = spmi_ext_register_writel(context, addr, val, len); + if (err) + goto err_out; + + addr += len; + val += len; + val_size -= len; + } + +err_out: + return err; } -static int regmap_spmi_write(void *context, const void *data, - size_t count) +static int regmap_spmi_ext_write(void *context, const void *data, + size_t count) { BUG_ON(count < 2); - return regmap_spmi_gather_write(context, data, 2, data + 2, count - 2); + return regmap_spmi_ext_gather_write(context, data, 2, data + 2, + count - 2); } -static struct regmap_bus regmap_spmi = { - .read = regmap_spmi_read, - .write = regmap_spmi_write, - .gather_write = regmap_spmi_gather_write, +static struct regmap_bus regmap_spmi_ext = { + .read = regmap_spmi_ext_read, + .write = regmap_spmi_ext_write, + .gather_write = regmap_spmi_ext_gather_write, .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, .val_format_endian_default = REGMAP_ENDIAN_NATIVE, }; /** - * regmap_init_spmi(): Initialize register map - * - * @sdev: Device that will be interacted with - * @config: Configuration for register map + * regmap_init_spmi_ext(): Create regmap for Ext register space + * @sdev: Device that will be interacted with + * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. */ -struct regmap *regmap_init_spmi(struct spmi_device *sdev, - const struct regmap_config *config) +struct regmap *regmap_init_spmi_ext(struct spmi_device *sdev, + const struct regmap_config *config) { - return regmap_init(&sdev->dev, ®map_spmi, sdev, config); + return regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config); } -EXPORT_SYMBOL_GPL(regmap_init_spmi); +EXPORT_SYMBOL_GPL(regmap_init_spmi_ext); /** - * devm_regmap_init_spmi(): Initialise managed register map - * - * @sdev: Device that will be interacted with - * @config: Configuration for register map + * devm_regmap_init_spmi_ext(): Create managed regmap for Ext register space + * @sdev: SPMI device that will be interacted with + * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer * to a struct regmap. The regmap will be automatically freed by the * device management code. */ -struct regmap *devm_regmap_init_spmi(struct spmi_device *sdev, +struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *sdev, const struct regmap_config *config) { - return devm_regmap_init(&sdev->dev, ®map_spmi, sdev, config); + return devm_regmap_init(&sdev->dev, ®map_spmi_ext, sdev, config); } -EXPORT_SYMBOL_GPL(devm_regmap_init_spmi); +EXPORT_SYMBOL_GPL(devm_regmap_init_spmi_ext); MODULE_LICENSE("GPL"); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6a19515f8a45..d0a072463a04 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -380,6 +380,28 @@ static void regmap_range_exit(struct regmap *map) kfree(map->selector_work_buf); } +int regmap_attach_dev(struct device *dev, struct regmap *map, + const struct regmap_config *config) +{ + struct regmap **m; + + map->dev = dev; + + regmap_debugfs_init(map, config->name); + + /* Add a devres resource for dev_get_regmap() */ + m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); + if (!m) { + regmap_debugfs_exit(map); + return -ENOMEM; + } + *m = map; + devres_add(dev, m); + + return 0; +} +EXPORT_SYMBOL_GPL(regmap_attach_dev); + /** * regmap_init(): Initialise register map * @@ -397,7 +419,7 @@ struct regmap *regmap_init(struct device *dev, void *bus_context, const struct regmap_config *config) { - struct regmap *map, **m; + struct regmap *map; int ret = -EINVAL; enum regmap_endian reg_endian, val_endian; int i, j; @@ -439,6 +461,7 @@ struct regmap *regmap_init(struct device *dev, else map->reg_stride = 1; map->use_single_rw = config->use_single_rw; + map->can_multi_write = config->can_multi_write; map->dev = dev; map->bus = bus; map->bus_context = bus_context; @@ -718,7 +741,7 @@ skip_format_initialization: new->window_start = range_cfg->window_start; new->window_len = range_cfg->window_len; - if (_regmap_range_add(map, new) == false) { + if (!_regmap_range_add(map, new)) { dev_err(map->dev, "Failed to add range %d\n", i); kfree(new); goto err_range; @@ -734,25 +757,18 @@ skip_format_initialization: } } - regmap_debugfs_init(map, config->name); - ret = regcache_init(map, config); if (ret != 0) goto err_range; - /* Add a devres resource for dev_get_regmap() */ - m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); - if (!m) { - ret = -ENOMEM; - goto err_debugfs; - } - *m = map; - devres_add(dev, m); + if (dev) + ret = regmap_attach_dev(dev, map, config); + if (ret != 0) + goto err_regcache; return map; -err_debugfs: - regmap_debugfs_exit(map); +err_regcache: regcache_exit(map); err_range: regmap_range_exit(map); @@ -1520,12 +1536,12 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (reg % map->reg_stride) return -EINVAL; - map->lock(map->lock_arg); /* * Some devices don't support bulk write, for * them we have a series of single write operations. */ if (!map->bus || map->use_single_rw) { + map->lock(map->lock_arg); for (i = 0; i < val_count; i++) { unsigned int ival; @@ -1554,31 +1570,239 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (ret != 0) goto out; } +out: + map->unlock(map->lock_arg); } else { void *wval; wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL); if (!wval) { - ret = -ENOMEM; dev_err(map->dev, "Error in memory allocation\n"); - goto out; + return -ENOMEM; } for (i = 0; i < val_count * val_bytes; i += val_bytes) map->format.parse_inplace(wval + i); + map->lock(map->lock_arg); ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + map->unlock(map->lock_arg); kfree(wval); } -out: - map->unlock(map->lock_arg); return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_write); /* + * _regmap_raw_multi_reg_write() + * + * the (register,newvalue) pairs in regs have not been formatted, but + * they are all in the same page and have been changed to being page + * relative. The page register has been written if that was neccessary. + */ +static int _regmap_raw_multi_reg_write(struct regmap *map, + const struct reg_default *regs, + size_t num_regs) +{ + int ret; + void *buf; + int i; + u8 *u8; + size_t val_bytes = map->format.val_bytes; + size_t reg_bytes = map->format.reg_bytes; + size_t pad_bytes = map->format.pad_bytes; + size_t pair_size = reg_bytes + pad_bytes + val_bytes; + size_t len = pair_size * num_regs; + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* We have to linearise by hand. */ + + u8 = buf; + + for (i = 0; i < num_regs; i++) { + int reg = regs[i].reg; + int val = regs[i].def; + trace_regmap_hw_write_start(map->dev, reg, 1); + map->format.format_reg(u8, reg, map->reg_shift); + u8 += reg_bytes + pad_bytes; + map->format.format_val(u8, val, 0); + u8 += val_bytes; + } + u8 = buf; + *u8 |= map->write_flag_mask; + + ret = map->bus->write(map->bus_context, buf, len); + + kfree(buf); + + for (i = 0; i < num_regs; i++) { + int reg = regs[i].reg; + trace_regmap_hw_write_done(map->dev, reg, 1); + } + return ret; +} + +static unsigned int _regmap_register_page(struct regmap *map, + unsigned int reg, + struct regmap_range_node *range) +{ + unsigned int win_page = (reg - range->range_min) / range->window_len; + + return win_page; +} + +static int _regmap_range_multi_paged_reg_write(struct regmap *map, + struct reg_default *regs, + size_t num_regs) +{ + int ret; + int i, n; + struct reg_default *base; + unsigned int this_page; + /* + * the set of registers are not neccessarily in order, but + * since the order of write must be preserved this algorithm + * chops the set each time the page changes + */ + base = regs; + for (i = 0, n = 0; i < num_regs; i++, n++) { + unsigned int reg = regs[i].reg; + struct regmap_range_node *range; + + range = _regmap_range_lookup(map, reg); + if (range) { + unsigned int win_page = _regmap_register_page(map, reg, + range); + + if (i == 0) + this_page = win_page; + if (win_page != this_page) { + this_page = win_page; + ret = _regmap_raw_multi_reg_write(map, base, n); + if (ret != 0) + return ret; + base += n; + n = 0; + } + ret = _regmap_select_page(map, &base[n].reg, range, 1); + if (ret != 0) + return ret; + } + } + if (n > 0) + return _regmap_raw_multi_reg_write(map, base, n); + return 0; +} + +static int _regmap_multi_reg_write(struct regmap *map, + const struct reg_default *regs, + size_t num_regs) +{ + int i; + int ret; + + if (!map->can_multi_write) { + for (i = 0; i < num_regs; i++) { + ret = _regmap_write(map, regs[i].reg, regs[i].def); + if (ret != 0) + return ret; + } + return 0; + } + + if (!map->format.parse_inplace) + return -EINVAL; + + if (map->writeable_reg) + for (i = 0; i < num_regs; i++) { + int reg = regs[i].reg; + if (!map->writeable_reg(map->dev, reg)) + return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; + } + + if (!map->cache_bypass) { + for (i = 0; i < num_regs; i++) { + unsigned int val = regs[i].def; + unsigned int reg = regs[i].reg; + ret = regcache_write(map, reg, val); + if (ret) { + dev_err(map->dev, + "Error in caching of register: %x ret: %d\n", + reg, ret); + return ret; + } + } + if (map->cache_only) { + map->cache_dirty = true; + return 0; + } + } + + WARN_ON(!map->bus); + + for (i = 0; i < num_regs; i++) { + unsigned int reg = regs[i].reg; + struct regmap_range_node *range; + range = _regmap_range_lookup(map, reg); + if (range) { + size_t len = sizeof(struct reg_default)*num_regs; + struct reg_default *base = kmemdup(regs, len, + GFP_KERNEL); + if (!base) + return -ENOMEM; + ret = _regmap_range_multi_paged_reg_write(map, base, + num_regs); + kfree(base); + + return ret; + } + } + return _regmap_raw_multi_reg_write(map, regs, num_regs); +} + +/* * regmap_multi_reg_write(): Write multiple registers to the device * + * where the set of register,value pairs are supplied in any order, + * possibly not all in a single range. + * + * @map: Register map to write to + * @regs: Array of structures containing register,value to be written + * @num_regs: Number of registers to write + * + * The 'normal' block write mode will send ultimately send data on the + * target bus as R,V1,V2,V3,..,Vn where successively higer registers are + * addressed. However, this alternative block multi write mode will send + * the data as R1,V1,R2,V2,..,Rn,Vn on the target bus. The target device + * must of course support the mode. + * + * A value of zero will be returned on success, a negative errno will be + * returned in error cases. + */ +int regmap_multi_reg_write(struct regmap *map, const struct reg_default *regs, + int num_regs) +{ + int ret; + + map->lock(map->lock_arg); + + ret = _regmap_multi_reg_write(map, regs, num_regs); + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_multi_reg_write); + +/* + * regmap_multi_reg_write_bypassed(): Write multiple registers to the + * device but not the cache + * * where the set of register are supplied in any order * * @map: Register map to write to @@ -1592,30 +1816,27 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); * A value of zero will be returned on success, a negative errno will * be returned in error cases. */ -int regmap_multi_reg_write(struct regmap *map, struct reg_default *regs, - int num_regs) +int regmap_multi_reg_write_bypassed(struct regmap *map, + const struct reg_default *regs, + int num_regs) { - int ret = 0, i; - - for (i = 0; i < num_regs; i++) { - int reg = regs[i].reg; - if (reg % map->reg_stride) - return -EINVAL; - } + int ret; + bool bypass; map->lock(map->lock_arg); - for (i = 0; i < num_regs; i++) { - ret = _regmap_write(map, regs[i].reg, regs[i].def); - if (ret != 0) - goto out; - } -out: + bypass = map->cache_bypass; + map->cache_bypass = true; + + ret = _regmap_multi_reg_write(map, regs, num_regs); + + map->cache_bypass = bypass; + map->unlock(map->lock_arg); return ret; } -EXPORT_SYMBOL_GPL(regmap_multi_reg_write); +EXPORT_SYMBOL_GPL(regmap_multi_reg_write_bypassed); /** * regmap_raw_write_async(): Write raw values to one or more registers @@ -1736,6 +1957,9 @@ static int _regmap_read(struct regmap *map, unsigned int reg, if (map->cache_only) return -EBUSY; + if (!regmap_readable(map, reg)) + return -EIO; + ret = map->reg_read(context, reg, val); if (ret == 0) { #ifdef LOG_DEVICE @@ -1966,9 +2190,11 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, if (tmp != orig) { ret = _regmap_write(map, reg, tmp); - *change = true; + if (change) + *change = true; } else { - *change = false; + if (change) + *change = false; } return ret; @@ -1987,11 +2213,10 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val) { - bool change; int ret; map->lock(map->lock_arg); - ret = _regmap_update_bits(map, reg, mask, val, &change); + ret = _regmap_update_bits(map, reg, mask, val, NULL); map->unlock(map->lock_arg); return ret; @@ -2016,14 +2241,13 @@ EXPORT_SYMBOL_GPL(regmap_update_bits); int regmap_update_bits_async(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val) { - bool change; int ret; map->lock(map->lock_arg); map->async = true; - ret = _regmap_update_bits(map, reg, mask, val, &change); + ret = _regmap_update_bits(map, reg, mask, val, NULL); map->async = false; @@ -2173,35 +2397,21 @@ EXPORT_SYMBOL_GPL(regmap_async_complete); * apply them immediately. Typically this is used to apply * corrections to be applied to the device defaults on startup, such * as the updates some vendors provide to undocumented registers. + * + * The caller must ensure that this function cannot be called + * concurrently with either itself or regcache_sync(). */ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs) { struct reg_default *p; - int i, ret; + int ret; bool bypass; if (WARN_ONCE(num_regs <= 0, "invalid registers number (%d)\n", num_regs)) return 0; - map->lock(map->lock_arg); - - bypass = map->cache_bypass; - - map->cache_bypass = true; - map->async = true; - - /* Write out first; it's useful to apply even if we fail later. */ - for (i = 0; i < num_regs; i++) { - ret = _regmap_write(map, regs[i].reg, regs[i].def); - if (ret != 0) { - dev_err(map->dev, "Failed to write %x = %x: %d\n", - regs[i].reg, regs[i].def, ret); - goto out; - } - } - p = krealloc(map->patch, sizeof(struct reg_default) * (map->patch_regs + num_regs), GFP_KERNEL); @@ -2210,9 +2420,20 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, map->patch = p; map->patch_regs += num_regs; } else { - ret = -ENOMEM; + return -ENOMEM; } + map->lock(map->lock_arg); + + bypass = map->cache_bypass; + + map->cache_bypass = true; + map->async = true; + + ret = _regmap_multi_reg_write(map, regs, num_regs); + if (ret != 0) + goto out; + out: map->async = false; map->cache_bypass = bypass; @@ -2240,6 +2461,18 @@ int regmap_get_val_bytes(struct regmap *map) } EXPORT_SYMBOL_GPL(regmap_get_val_bytes); +int regmap_parse_val(struct regmap *map, const void *buf, + unsigned int *val) +{ + if (!map->format.parse_val) + return -EINVAL; + + *val = map->format.parse_val(buf); + + return 0; +} +EXPORT_SYMBOL_GPL(regmap_parse_val); + static int __init regmap_initcall(void) { regmap_debugfs_initcall(); diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 8184451b57c0..422b7d84f686 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -874,7 +874,7 @@ bio_pageinc(struct bio *bio) /* Non-zero page count for non-head members of * compound pages is no longer allowed by the kernel. */ - page = compound_trans_head(bv.bv_page); + page = compound_head(bv.bv_page); atomic_inc(&page->_count); } } @@ -887,7 +887,7 @@ bio_pagedec(struct bio *bio) struct bvec_iter iter; bio_for_each_segment(bv, bio, iter) { - page = compound_trans_head(bv.bv_page); + page = compound_head(bv.bv_page); atomic_dec(&page->_count); } } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 2023043ce7c0..8f5565bf34cd 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -961,17 +961,31 @@ static void empty(void) { } -static DECLARE_WORK(floppy_work, NULL); +static void (*floppy_work_fn)(void); + +static void floppy_work_workfn(struct work_struct *work) +{ + floppy_work_fn(); +} + +static DECLARE_WORK(floppy_work, floppy_work_workfn); static void schedule_bh(void (*handler)(void)) { WARN_ON(work_pending(&floppy_work)); - PREPARE_WORK(&floppy_work, (work_func_t)handler); + floppy_work_fn = handler; queue_work(floppy_wq, &floppy_work); } -static DECLARE_DELAYED_WORK(fd_timer, NULL); +static void (*fd_timer_fn)(void) = NULL; + +static void fd_timer_workfn(struct work_struct *work) +{ + fd_timer_fn(); +} + +static DECLARE_DELAYED_WORK(fd_timer, fd_timer_workfn); static void cancel_activity(void) { @@ -982,7 +996,7 @@ static void cancel_activity(void) /* this function makes sure that the disk stays in the drive during the * transfer */ -static void fd_watchdog(struct work_struct *arg) +static void fd_watchdog(void) { debug_dcl(DP->flags, "calling disk change from watchdog\n"); @@ -993,7 +1007,7 @@ static void fd_watchdog(struct work_struct *arg) reset_fdc(); } else { cancel_delayed_work(&fd_timer); - PREPARE_DELAYED_WORK(&fd_timer, fd_watchdog); + fd_timer_fn = fd_watchdog; queue_delayed_work(floppy_wq, &fd_timer, HZ / 10); } } @@ -1005,7 +1019,8 @@ static void main_command_interrupt(void) } /* waits for a delay (spinup or select) to pass */ -static int fd_wait_for_completion(unsigned long expires, work_func_t function) +static int fd_wait_for_completion(unsigned long expires, + void (*function)(void)) { if (FDCS->reset) { reset_fdc(); /* do the reset during sleep to win time @@ -1016,7 +1031,7 @@ static int fd_wait_for_completion(unsigned long expires, work_func_t function) if (time_before(jiffies, expires)) { cancel_delayed_work(&fd_timer); - PREPARE_DELAYED_WORK(&fd_timer, function); + fd_timer_fn = function; queue_delayed_work(floppy_wq, &fd_timer, expires - jiffies); return 1; } @@ -1334,8 +1349,7 @@ static int fdc_dtr(void) * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies) */ FDCS->dtr = raw_cmd->rate & 3; - return fd_wait_for_completion(jiffies + 2UL * HZ / 100, - (work_func_t)floppy_ready); + return fd_wait_for_completion(jiffies + 2UL * HZ / 100, floppy_ready); } /* fdc_dtr */ static void tell_sector(void) @@ -1440,7 +1454,7 @@ static void setup_rw_floppy(void) int flags; int dflags; unsigned long ready_date; - work_func_t function; + void (*function)(void); flags = raw_cmd->flags; if (flags & (FD_RAW_READ | FD_RAW_WRITE)) @@ -1454,9 +1468,9 @@ static void setup_rw_floppy(void) */ if (time_after(ready_date, jiffies + DP->select_delay)) { ready_date -= DP->select_delay; - function = (work_func_t)floppy_start; + function = floppy_start; } else - function = (work_func_t)setup_rw_floppy; + function = setup_rw_floppy; /* wait until the floppy is spinning fast enough */ if (fd_wait_for_completion(ready_date, function)) @@ -1486,7 +1500,7 @@ static void setup_rw_floppy(void) inr = result(); cont->interrupt(); } else if (flags & FD_RAW_NEED_DISK) - fd_watchdog(NULL); + fd_watchdog(); } static int blind_seek; @@ -1863,7 +1877,7 @@ static int start_motor(void (*function)(void)) /* wait_for_completion also schedules reset if needed. */ return fd_wait_for_completion(DRS->select_date + DP->select_delay, - (work_func_t)function); + function); } static void floppy_ready(void) diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 516026954be6..d777bb7cea93 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -4498,7 +4498,7 @@ static int mtip_pci_probe(struct pci_dev *pdev, } dev_info(&pdev->dev, "NUMA node %d (closest: %d,%d, probe on %d:%d)\n", my_node, pcibus_to_node(pdev->bus), dev_to_node(&pdev->dev), - cpu_to_node(smp_processor_id()), smp_processor_id()); + cpu_to_node(raw_smp_processor_id()), raw_smp_processor_id()); dd = kzalloc_node(sizeof(struct driver_data), GFP_KERNEL, my_node); if (dd == NULL) { diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index b52e9a6d6aad..54174cb32feb 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -53,7 +53,7 @@ #define MTIP_FTL_REBUILD_TIMEOUT_MS 2400000 /* unaligned IO handling */ -#define MTIP_MAX_UNALIGNED_SLOTS 8 +#define MTIP_MAX_UNALIGNED_SLOTS 2 /* Macro to extract the tag bit number from a tag value. */ #define MTIP_TAG_BIT(tag) (tag & 0x1F) diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index 51824d1f23ea..8459e4e7c719 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -993,7 +993,7 @@ static void nvme_abort_cmd(int cmdid, struct nvme_queue *nvmeq) dev_warn(&dev->pci_dev->dev, "I/O %d QID %d timeout, reset controller\n", cmdid, nvmeq->qid); - PREPARE_WORK(&dev->reset_work, nvme_reset_failed_dev); + dev->reset_workfn = nvme_reset_failed_dev; queue_work(nvme_workq, &dev->reset_work); return; } @@ -1696,8 +1696,7 @@ static int nvme_kthread(void *data) list_del_init(&dev->node); dev_warn(&dev->pci_dev->dev, "Failed status, reset controller\n"); - PREPARE_WORK(&dev->reset_work, - nvme_reset_failed_dev); + dev->reset_workfn = nvme_reset_failed_dev; queue_work(nvme_workq, &dev->reset_work); continue; } @@ -2406,7 +2405,7 @@ static int nvme_dev_resume(struct nvme_dev *dev) return ret; if (ret == -EBUSY) { spin_lock(&dev_list_lock); - PREPARE_WORK(&dev->reset_work, nvme_remove_disks); + dev->reset_workfn = nvme_remove_disks; queue_work(nvme_workq, &dev->reset_work); spin_unlock(&dev_list_lock); } @@ -2435,6 +2434,12 @@ static void nvme_reset_failed_dev(struct work_struct *ws) nvme_dev_reset(dev); } +static void nvme_reset_workfn(struct work_struct *work) +{ + struct nvme_dev *dev = container_of(work, struct nvme_dev, reset_work); + dev->reset_workfn(work); +} + static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int result = -ENOMEM; @@ -2453,7 +2458,8 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto free; INIT_LIST_HEAD(&dev->namespaces); - INIT_WORK(&dev->reset_work, nvme_reset_failed_dev); + dev->reset_workfn = nvme_reset_failed_dev; + INIT_WORK(&dev->reset_work, nvme_reset_workfn); dev->pci_dev = pdev; pci_set_drvdata(pdev, dev); result = nvme_set_instance(dev); @@ -2553,7 +2559,7 @@ static int nvme_resume(struct device *dev) struct nvme_dev *ndev = pci_get_drvdata(pdev); if (nvme_dev_resume(ndev) && !work_busy(&ndev->reset_work)) { - PREPARE_WORK(&ndev->reset_work, nvme_reset_failed_dev); + ndev->reset_workfn = nvme_reset_failed_dev; queue_work(nvme_workq, &ndev->reset_work); } return 0; diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index b365e0dfccb6..34898d53395b 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2109,7 +2109,6 @@ static void rbd_img_obj_callback(struct rbd_obj_request *obj_request) rbd_assert(img_request->obj_request_count > 0); rbd_assert(which != BAD_WHICH); rbd_assert(which < img_request->obj_request_count); - rbd_assert(which >= img_request->next_completion); spin_lock_irq(&img_request->completion_lock); if (which != img_request->next_completion) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 011e55d820b1..51c557cfd92b 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -612,6 +612,8 @@ static ssize_t disksize_store(struct device *dev, disksize = PAGE_ALIGN(disksize); meta = zram_meta_alloc(disksize); + if (!meta) + return -ENOMEM; down_write(&zram->init_lock); if (zram->init_done) { up_write(&zram->init_lock); diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 725c46162bbd..2ac754e18bcf 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -870,14 +870,14 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, ret = of_property_read_u32_array(np, "pcie-mem-aperture", reg, ARRAY_SIZE(reg)); if (!ret) { mem->start = reg[0]; - mem->end = mem->start + reg[1]; + mem->end = mem->start + reg[1] - 1; mem->flags = IORESOURCE_MEM; } ret = of_property_read_u32_array(np, "pcie-io-aperture", reg, ARRAY_SIZE(reg)); if (!ret) { io->start = reg[0]; - io->end = io->start + reg[1]; + io->end = io->start + reg[1] - 1; io->flags = IORESOURCE_IO; } } diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c index 1b192395a90c..8121b4c70ede 100644 --- a/drivers/char/agp/frontend.c +++ b/drivers/char/agp/frontend.c @@ -31,7 +31,6 @@ #include <linux/module.h> #include <linux/mman.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/miscdevice.h> #include <linux/agp_backend.h> #include <linux/agpgart.h> diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index f39437addb58..0fbccce1cee9 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -29,7 +29,6 @@ */ #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/pagemap.h> #include <linux/miscdevice.h> #include <linux/pm.h> diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 5c85350f4c3d..9a024f899dd4 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -17,7 +17,6 @@ #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/pagemap.h> #include <linux/agp_backend.h> diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c index 05b8d0241bde..3051c73bc383 100644 --- a/drivers/char/agp/sgi-agp.c +++ b/drivers/char/agp/sgi-agp.c @@ -15,7 +15,6 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/agp_backend.h> #include <asm/sn/addrs.h> #include <asm/sn/io.h> diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c index 43577ca780e3..8c3b255e629a 100644 --- a/drivers/char/hw_random/bcm2835-rng.c +++ b/drivers/char/hw_random/bcm2835-rng.c @@ -8,7 +8,6 @@ */ #include <linux/hw_random.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index a0f7724852eb..b9495a8c05c6 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -37,7 +37,6 @@ #include <linux/kernel.h> #include <linux/fs.h> #include <linux/sched.h> -#include <linux/init.h> #include <linux/miscdevice.h> #include <linux/delay.h> #include <linux/slab.h> diff --git a/drivers/char/hw_random/exynos-rng.c b/drivers/char/hw_random/exynos-rng.c index 402ccfb625c5..9f8277cc44b4 100644 --- a/drivers/char/hw_random/exynos-rng.c +++ b/drivers/char/hw_random/exynos-rng.c @@ -22,7 +22,6 @@ #include <linux/hw_random.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/platform_device.h> #include <linux/clk.h> diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index f9beed54d0c8..432232eefe05 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -7,7 +7,6 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/delay.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/preempt.h> diff --git a/drivers/char/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c index 232b87fb5fc9..00e9d2d46634 100644 --- a/drivers/char/hw_random/nomadik-rng.c +++ b/drivers/char/hw_random/nomadik-rng.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/device.h> #include <linux/amba/bus.h> #include <linux/hw_random.h> diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c index f2885dbe1849..b5cc3420c659 100644 --- a/drivers/char/hw_random/octeon-rng.c +++ b/drivers/char/hw_random/octeon-rng.c @@ -10,7 +10,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/device.h> #include <linux/hw_random.h> diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 03f41896d090..b7efd3c1a882 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -61,7 +61,6 @@ #include <linux/ipmi_smi.h> #include <asm/io.h> #include "ipmi_si_sm.h" -#include <linux/init.h> #include <linux/dmi.h> #include <linux/string.h> #include <linux/ctype.h> diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 92c5937f80c3..917403fe10da 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -99,6 +99,9 @@ static ssize_t read_mem(struct file *file, char __user *buf, ssize_t read, sz; char *ptr; + if (p != *ppos) + return 0; + if (!valid_phys_addr_range(p, count)) return -EFAULT; read = 0; @@ -157,6 +160,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf, unsigned long copied; void *ptr; + if (p != *ppos) + return -EFBIG; + if (!valid_phys_addr_range(p, count)) return -EFAULT; diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c index 881c9e595939..28740046bc83 100644 --- a/drivers/char/mwave/3780i.c +++ b/drivers/char/mwave/3780i.c @@ -50,7 +50,6 @@ #include <linux/unistd.h> #include <linux/delay.h> #include <linux/ioport.h> -#include <linux/init.h> #include <linux/bitops.h> #include <linux/sched.h> /* cond_resched() */ diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c index 0e506bad1986..bd377472dcfb 100644 --- a/drivers/char/tile-srom.c +++ b/drivers/char/tile-srom.c @@ -20,7 +20,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 52b9b2b2f300..472af4bb1b61 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -21,7 +21,6 @@ * * */ -#include <linux/init.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/wait.h> diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index 5b0dd8ef74c0..3b7bf2162898 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -38,7 +38,6 @@ #include <linux/miscdevice.h> #include <linux/kernel.h> #include <linux/delay.h> -#include <linux/init.h> #include <linux/wait.h> #include <linux/string.h> #include <linux/interrupt.h> diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c index bd313f7816a8..c1af80bcdf20 100644 --- a/drivers/clk/at91/clk-master.c +++ b/drivers/clk/at91/clk-master.c @@ -242,7 +242,7 @@ of_at91_clk_master_setup(struct device_node *np, struct at91_pmc *pmc, irq = irq_of_parse_and_map(np, 0); if (!irq) - return; + goto out_free_characteristics; clk = at91_clk_register_master(pmc, irq, name, num_parents, parent_names, layout, diff --git a/drivers/clk/clk-nomadik.c b/drivers/clk/clk-nomadik.c index 6a934a5296bd..05e04ce0f148 100644 --- a/drivers/clk/clk-nomadik.c +++ b/drivers/clk/clk-nomadik.c @@ -494,6 +494,9 @@ static const struct file_operations nomadik_src_clk_debugfs_ops = { static int __init nomadik_src_clk_init_debugfs(void) { + /* Vital for multiplatform */ + if (!src_base) + return -ENODEV; src_pcksr0_boot = readl(src_base + SRC_PCKSR0); src_pcksr1_boot = readl(src_base + SRC_PCKSR1); debugfs_create_file("nomadik-src-clk", S_IFREG | S_IRUGO, diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 5517944495d8..c42e608af6bb 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2226,24 +2226,25 @@ EXPORT_SYMBOL_GPL(devm_clk_unregister); */ int __clk_get(struct clk *clk) { - if (clk && !try_module_get(clk->owner)) - return 0; + if (clk) { + if (!try_module_get(clk->owner)) + return 0; - kref_get(&clk->ref); + kref_get(&clk->ref); + } return 1; } void __clk_put(struct clk *clk) { - if (WARN_ON_ONCE(IS_ERR(clk))) + if (!clk || WARN_ON_ONCE(IS_ERR(clk))) return; clk_prepare_lock(); kref_put(&clk->ref, __clk_release); clk_prepare_unlock(); - if (clk) - module_put(clk->owner); + module_put(clk->owner); } /*** clk rate change notifiers ***/ diff --git a/drivers/clk/keystone/gate.c b/drivers/clk/keystone/gate.c index 17a598398a53..86f1e362eafb 100644 --- a/drivers/clk/keystone/gate.c +++ b/drivers/clk/keystone/gate.c @@ -179,6 +179,7 @@ static struct clk *clk_register_psc(struct device *dev, init.name = name; init.ops = &clk_psc_ops; + init.flags = 0; init.parent_names = (parent_name ? &parent_name : NULL); init.num_parents = (parent_name ? 1 : 0); diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index 81a202d12a7a..bef198a83863 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -141,13 +141,6 @@ static const struct coreclk_soc_desc a370_coreclks = { .num_ratios = ARRAY_SIZE(a370_coreclk_ratios), }; -static void __init a370_coreclk_init(struct device_node *np) -{ - mvebu_coreclk_setup(np, &a370_coreclks); -} -CLK_OF_DECLARE(a370_core_clk, "marvell,armada-370-core-clock", - a370_coreclk_init); - /* * Clock Gating Control */ @@ -168,9 +161,15 @@ static const struct clk_gating_soc_desc a370_gating_desc[] __initconst = { { } }; -static void __init a370_clk_gating_init(struct device_node *np) +static void __init a370_clk_init(struct device_node *np) { - mvebu_clk_gating_setup(np, a370_gating_desc); + struct device_node *cgnp = + of_find_compatible_node(NULL, NULL, "marvell,armada-370-gating-clock"); + + mvebu_coreclk_setup(np, &a370_coreclks); + + if (cgnp) + mvebu_clk_gating_setup(cgnp, a370_gating_desc); } -CLK_OF_DECLARE(a370_clk_gating, "marvell,armada-370-gating-clock", - a370_clk_gating_init); +CLK_OF_DECLARE(a370_clk, "marvell,armada-370-core-clock", a370_clk_init); + diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c index 9922c4475aa8..b3094315a3c0 100644 --- a/drivers/clk/mvebu/armada-xp.c +++ b/drivers/clk/mvebu/armada-xp.c @@ -158,13 +158,6 @@ static const struct coreclk_soc_desc axp_coreclks = { .num_ratios = ARRAY_SIZE(axp_coreclk_ratios), }; -static void __init axp_coreclk_init(struct device_node *np) -{ - mvebu_coreclk_setup(np, &axp_coreclks); -} -CLK_OF_DECLARE(axp_core_clk, "marvell,armada-xp-core-clock", - axp_coreclk_init); - /* * Clock Gating Control */ @@ -202,9 +195,14 @@ static const struct clk_gating_soc_desc axp_gating_desc[] __initconst = { { } }; -static void __init axp_clk_gating_init(struct device_node *np) +static void __init axp_clk_init(struct device_node *np) { - mvebu_clk_gating_setup(np, axp_gating_desc); + struct device_node *cgnp = + of_find_compatible_node(NULL, NULL, "marvell,armada-xp-gating-clock"); + + mvebu_coreclk_setup(np, &axp_coreclks); + + if (cgnp) + mvebu_clk_gating_setup(cgnp, axp_gating_desc); } -CLK_OF_DECLARE(axp_clk_gating, "marvell,armada-xp-gating-clock", - axp_clk_gating_init); +CLK_OF_DECLARE(axp_clk, "marvell,armada-xp-core-clock", axp_clk_init); diff --git a/drivers/clk/mvebu/dove.c b/drivers/clk/mvebu/dove.c index 38aee1e3f242..b8c2424ac926 100644 --- a/drivers/clk/mvebu/dove.c +++ b/drivers/clk/mvebu/dove.c @@ -154,12 +154,6 @@ static const struct coreclk_soc_desc dove_coreclks = { .num_ratios = ARRAY_SIZE(dove_coreclk_ratios), }; -static void __init dove_coreclk_init(struct device_node *np) -{ - mvebu_coreclk_setup(np, &dove_coreclks); -} -CLK_OF_DECLARE(dove_core_clk, "marvell,dove-core-clock", dove_coreclk_init); - /* * Clock Gating Control */ @@ -186,9 +180,14 @@ static const struct clk_gating_soc_desc dove_gating_desc[] __initconst = { { } }; -static void __init dove_clk_gating_init(struct device_node *np) +static void __init dove_clk_init(struct device_node *np) { - mvebu_clk_gating_setup(np, dove_gating_desc); + struct device_node *cgnp = + of_find_compatible_node(NULL, NULL, "marvell,dove-gating-clock"); + + mvebu_coreclk_setup(np, &dove_coreclks); + + if (cgnp) + mvebu_clk_gating_setup(cgnp, dove_gating_desc); } -CLK_OF_DECLARE(dove_clk_gating, "marvell,dove-gating-clock", - dove_clk_gating_init); +CLK_OF_DECLARE(dove_clk, "marvell,dove-core-clock", dove_clk_init); diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c index 2636a55f29f9..ddb666a86500 100644 --- a/drivers/clk/mvebu/kirkwood.c +++ b/drivers/clk/mvebu/kirkwood.c @@ -193,13 +193,6 @@ static const struct coreclk_soc_desc kirkwood_coreclks = { .num_ratios = ARRAY_SIZE(kirkwood_coreclk_ratios), }; -static void __init kirkwood_coreclk_init(struct device_node *np) -{ - mvebu_coreclk_setup(np, &kirkwood_coreclks); -} -CLK_OF_DECLARE(kirkwood_core_clk, "marvell,kirkwood-core-clock", - kirkwood_coreclk_init); - static const struct coreclk_soc_desc mv88f6180_coreclks = { .get_tclk_freq = kirkwood_get_tclk_freq, .get_cpu_freq = mv88f6180_get_cpu_freq, @@ -208,13 +201,6 @@ static const struct coreclk_soc_desc mv88f6180_coreclks = { .num_ratios = ARRAY_SIZE(kirkwood_coreclk_ratios), }; -static void __init mv88f6180_coreclk_init(struct device_node *np) -{ - mvebu_coreclk_setup(np, &mv88f6180_coreclks); -} -CLK_OF_DECLARE(mv88f6180_core_clk, "marvell,mv88f6180-core-clock", - mv88f6180_coreclk_init); - /* * Clock Gating Control */ @@ -239,9 +225,21 @@ static const struct clk_gating_soc_desc kirkwood_gating_desc[] __initconst = { { } }; -static void __init kirkwood_clk_gating_init(struct device_node *np) +static void __init kirkwood_clk_init(struct device_node *np) { - mvebu_clk_gating_setup(np, kirkwood_gating_desc); + struct device_node *cgnp = + of_find_compatible_node(NULL, NULL, "marvell,kirkwood-gating-clock"); + + + if (of_device_is_compatible(np, "marvell,mv88f6180-core-clock")) + mvebu_coreclk_setup(np, &mv88f6180_coreclks); + else + mvebu_coreclk_setup(np, &kirkwood_coreclks); + + if (cgnp) + mvebu_clk_gating_setup(cgnp, kirkwood_gating_desc); } -CLK_OF_DECLARE(kirkwood_clk_gating, "marvell,kirkwood-gating-clock", - kirkwood_clk_gating_init); +CLK_OF_DECLARE(kirkwood_clk, "marvell,kirkwood-core-clock", + kirkwood_clk_init); +CLK_OF_DECLARE(mv88f6180_clk, "marvell,mv88f6180-core-clock", + kirkwood_clk_init); diff --git a/drivers/clk/shmobile/clk-rcar-gen2.c b/drivers/clk/shmobile/clk-rcar-gen2.c index a59ec217a124..99c27b1c625b 100644 --- a/drivers/clk/shmobile/clk-rcar-gen2.c +++ b/drivers/clk/shmobile/clk-rcar-gen2.c @@ -26,6 +26,8 @@ struct rcar_gen2_cpg { void __iomem *reg; }; +#define CPG_FRQCRB 0x00000004 +#define CPG_FRQCRB_KICK BIT(31) #define CPG_SDCKCR 0x00000074 #define CPG_PLL0CR 0x000000d8 #define CPG_FRQCRC 0x000000e0 @@ -45,6 +47,7 @@ struct rcar_gen2_cpg { struct cpg_z_clk { struct clk_hw hw; void __iomem *reg; + void __iomem *kick_reg; }; #define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw) @@ -83,17 +86,45 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, { struct cpg_z_clk *zclk = to_z_clk(hw); unsigned int mult; - u32 val; + u32 val, kick; + unsigned int i; mult = div_u64((u64)rate * 32, parent_rate); mult = clamp(mult, 1U, 32U); + if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK) + return -EBUSY; + val = clk_readl(zclk->reg); val &= ~CPG_FRQCRC_ZFC_MASK; val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT; clk_writel(val, zclk->reg); - return 0; + /* + * Set KICK bit in FRQCRB to update hardware setting and wait for + * clock change completion. + */ + kick = clk_readl(zclk->kick_reg); + kick |= CPG_FRQCRB_KICK; + clk_writel(kick, zclk->kick_reg); + + /* + * Note: There is no HW information about the worst case latency. + * + * Using experimental measurements, it seems that no more than + * ~10 iterations are needed, independently of the CPU rate. + * Since this value might be dependant of external xtal rate, pll1 + * rate or even the other emulation clocks rate, use 1000 as a + * "super" safe value. + */ + for (i = 1000; i; i--) { + if (!(clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)) + return 0; + + cpu_relax(); + } + + return -ETIMEDOUT; } static const struct clk_ops cpg_z_clk_ops = { @@ -120,6 +151,7 @@ static struct clk * __init cpg_z_clk_register(struct rcar_gen2_cpg *cpg) init.num_parents = 1; zclk->reg = cpg->reg + CPG_FRQCRC; + zclk->kick_reg = cpg->reg + CPG_FRQCRB; zclk->hw.init = &init; clk = clk_register(NULL, &zclk->hw); @@ -186,7 +218,7 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg, const char *name) { const struct clk_div_table *table = NULL; - const char *parent_name = "main"; + const char *parent_name; unsigned int shift; unsigned int mult = 1; unsigned int div = 1; @@ -201,23 +233,31 @@ rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg, * the multiplier value. */ u32 value = clk_readl(cpg->reg + CPG_PLL0CR); + parent_name = "main"; mult = ((value >> 24) & ((1 << 7) - 1)) + 1; } else if (!strcmp(name, "pll1")) { + parent_name = "main"; mult = config->pll1_mult / 2; } else if (!strcmp(name, "pll3")) { + parent_name = "main"; mult = config->pll3_mult; } else if (!strcmp(name, "lb")) { + parent_name = "pll1_div2"; div = cpg_mode & BIT(18) ? 36 : 24; } else if (!strcmp(name, "qspi")) { + parent_name = "pll1_div2"; div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2) - ? 16 : 20; + ? 8 : 10; } else if (!strcmp(name, "sdh")) { + parent_name = "pll1_div2"; table = cpg_sdh_div_table; shift = 8; } else if (!strcmp(name, "sd0")) { + parent_name = "pll1_div2"; table = cpg_sd01_div_table; shift = 4; } else if (!strcmp(name, "sd1")) { + parent_name = "pll1_div2"; table = cpg_sd01_div_table; shift = 0; } else if (!strcmp(name, "z")) { diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c index 4d75b1f37e3a..290f9c1a3749 100644 --- a/drivers/clk/tegra/clk-divider.c +++ b/drivers/clk/tegra/clk-divider.c @@ -59,7 +59,7 @@ static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate, return 0; if (divider_ux1 > get_max_div(divider)) - return -EINVAL; + return get_max_div(divider); return divider_ux1; } diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h index cf0c323f2c36..c39613c519af 100644 --- a/drivers/clk/tegra/clk-id.h +++ b/drivers/clk/tegra/clk-id.h @@ -180,9 +180,13 @@ enum clk_id { tegra_clk_sbc6_8, tegra_clk_sclk, tegra_clk_sdmmc1, + tegra_clk_sdmmc1_8, tegra_clk_sdmmc2, + tegra_clk_sdmmc2_8, tegra_clk_sdmmc3, + tegra_clk_sdmmc3_8, tegra_clk_sdmmc4, + tegra_clk_sdmmc4_8, tegra_clk_se, tegra_clk_soc_therm, tegra_clk_sor0, diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index 5c35885f4a7c..1fa5c3f33b20 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -371,9 +371,7 @@ static const char *mux_pllp3_pllc_clkm[] = { static const char *mux_pllm_pllc_pllp_plla_pllc2_c3_clkm[] = { "pll_m", "pll_c", "pll_p", "pll_a", "pll_c2", "pll_c3", "clk_m" }; -static u32 mux_pllm_pllc_pllp_plla_pllc2_c3_clkm_idx[] = { - [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 6, -}; +#define mux_pllm_pllc_pllp_plla_pllc2_c3_clkm_idx NULL static const char *mux_pllm_pllc2_c_c3_pllp_plla_pllc4[] = { "pll_m", "pll_c2", "pll_c", "pll_c3", "pll_p", "pll_a_out0", "pll_c4", @@ -465,6 +463,10 @@ static struct tegra_periph_init_data periph_clks[] = { MUX("adx1", mux_plla_pllc_pllp_clkm, CLK_SOURCE_ADX1, 180, TEGRA_PERIPH_ON_APB, tegra_clk_adx1), MUX("amx1", mux_plla_pllc_pllp_clkm, CLK_SOURCE_AMX1, 185, TEGRA_PERIPH_ON_APB, tegra_clk_amx1), MUX("vi_sensor2", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR2, 20, TEGRA_PERIPH_NO_RESET, tegra_clk_vi_sensor2), + MUX8("sdmmc1", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC1, 14, 0, tegra_clk_sdmmc1_8), + MUX8("sdmmc2", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC2, 9, 0, tegra_clk_sdmmc2_8), + MUX8("sdmmc3", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC3, 69, 0, tegra_clk_sdmmc3_8), + MUX8("sdmmc4", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SDMMC4, 15, 0, tegra_clk_sdmmc4_8), MUX8("sbc1", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC1, 41, TEGRA_PERIPH_ON_APB, tegra_clk_sbc1_8), MUX8("sbc2", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC2, 44, TEGRA_PERIPH_ON_APB, tegra_clk_sbc2_8), MUX8("sbc3", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SBC3, 46, TEGRA_PERIPH_ON_APB, tegra_clk_sbc3_8), @@ -492,7 +494,7 @@ static struct tegra_periph_init_data periph_clks[] = { UART("uartb", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, tegra_clk_uartb), UART("uartc", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, tegra_clk_uartc), UART("uartd", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTD, 65, tegra_clk_uartd), - UART("uarte", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTE, 65, tegra_clk_uarte), + UART("uarte", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTE, 66, tegra_clk_uarte), XUSB("xusb_host_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_host_src), XUSB("xusb_falcon_src", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_falcon_src), XUSB("xusb_fs_src", mux_clkm_48M_pllp_480M, CLK_SOURCE_XUSB_FS_SRC, 143, TEGRA_PERIPH_NO_RESET, tegra_clk_xusb_fs_src), diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c index 05dce4aa2c11..feb3201c85ce 100644 --- a/drivers/clk/tegra/clk-tegra-super-gen4.c +++ b/drivers/clk/tegra/clk-tegra-super-gen4.c @@ -120,7 +120,7 @@ void __init tegra_super_clk_gen4_init(void __iomem *clk_base, ARRAY_SIZE(cclk_lp_parents), CLK_SET_RATE_PARENT, clk_base + CCLKLP_BURST_POLICY, - 0, 4, 8, 9, NULL); + TEGRA_DIVIDER_2, 4, 8, 9, NULL); *dt_clk = clk; } diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 90d9d25f2228..80431f0fb268 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -682,12 +682,12 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = { [tegra_clk_timer] = { .dt_id = TEGRA114_CLK_TIMER, .present = true }, [tegra_clk_uarta] = { .dt_id = TEGRA114_CLK_UARTA, .present = true }, [tegra_clk_uartd] = { .dt_id = TEGRA114_CLK_UARTD, .present = true }, - [tegra_clk_sdmmc2] = { .dt_id = TEGRA114_CLK_SDMMC2, .present = true }, + [tegra_clk_sdmmc2_8] = { .dt_id = TEGRA114_CLK_SDMMC2, .present = true }, [tegra_clk_i2s1] = { .dt_id = TEGRA114_CLK_I2S1, .present = true }, [tegra_clk_i2c1] = { .dt_id = TEGRA114_CLK_I2C1, .present = true }, [tegra_clk_ndflash] = { .dt_id = TEGRA114_CLK_NDFLASH, .present = true }, - [tegra_clk_sdmmc1] = { .dt_id = TEGRA114_CLK_SDMMC1, .present = true }, - [tegra_clk_sdmmc4] = { .dt_id = TEGRA114_CLK_SDMMC4, .present = true }, + [tegra_clk_sdmmc1_8] = { .dt_id = TEGRA114_CLK_SDMMC1, .present = true }, + [tegra_clk_sdmmc4_8] = { .dt_id = TEGRA114_CLK_SDMMC4, .present = true }, [tegra_clk_pwm] = { .dt_id = TEGRA114_CLK_PWM, .present = true }, [tegra_clk_i2s0] = { .dt_id = TEGRA114_CLK_I2S0, .present = true }, [tegra_clk_i2s2] = { .dt_id = TEGRA114_CLK_I2S2, .present = true }, @@ -723,7 +723,7 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = { [tegra_clk_bsev] = { .dt_id = TEGRA114_CLK_BSEV, .present = true }, [tegra_clk_i2c3] = { .dt_id = TEGRA114_CLK_I2C3, .present = true }, [tegra_clk_sbc4_8] = { .dt_id = TEGRA114_CLK_SBC4, .present = true }, - [tegra_clk_sdmmc3] = { .dt_id = TEGRA114_CLK_SDMMC3, .present = true }, + [tegra_clk_sdmmc3_8] = { .dt_id = TEGRA114_CLK_SDMMC3, .present = true }, [tegra_clk_owr] = { .dt_id = TEGRA114_CLK_OWR, .present = true }, [tegra_clk_csite] = { .dt_id = TEGRA114_CLK_CSITE, .present = true }, [tegra_clk_la] = { .dt_id = TEGRA114_CLK_LA, .present = true }, diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index aff86b5bc745..166e02f16c8a 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -516,11 +516,11 @@ static struct div_nmp pllp_nmp = { }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { - {12000000, 216000000, 432, 12, 1, 8}, - {13000000, 216000000, 432, 13, 1, 8}, - {16800000, 216000000, 360, 14, 1, 8}, - {19200000, 216000000, 360, 16, 1, 8}, - {26000000, 216000000, 432, 26, 1, 8}, + {12000000, 408000000, 408, 12, 0, 8}, + {13000000, 408000000, 408, 13, 0, 8}, + {16800000, 408000000, 340, 14, 0, 8}, + {19200000, 408000000, 340, 16, 0, 8}, + {26000000, 408000000, 408, 26, 0, 8}, {0, 0, 0, 0, 0, 0}, }; @@ -570,6 +570,15 @@ static struct tegra_clk_pll_params pll_a_params = { .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_USE_LOCK, }; +static struct div_nmp plld_nmp = { + .divm_shift = 0, + .divm_width = 5, + .divn_shift = 8, + .divn_width = 11, + .divp_shift = 20, + .divp_width = 3, +}; + static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { {12000000, 216000000, 864, 12, 4, 12}, {13000000, 216000000, 864, 13, 4, 12}, @@ -603,19 +612,18 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, - .div_nmp = &pllp_nmp, + .div_nmp = &plld_nmp, .freq_table = pll_d_freq_table, .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_SET_LFCON | TEGRA_PLL_USE_LOCK, }; static struct tegra_clk_pll_freq_table tegra124_pll_d2_freq_table[] = { - { 12000000, 148500000, 99, 1, 8}, - { 12000000, 594000000, 99, 1, 1}, - { 13000000, 594000000, 91, 1, 1}, /* actual: 591.5 MHz */ - { 16800000, 594000000, 71, 1, 1}, /* actual: 596.4 MHz */ - { 19200000, 594000000, 62, 1, 1}, /* actual: 595.2 MHz */ - { 26000000, 594000000, 91, 2, 1}, /* actual: 591.5 MHz */ + { 12000000, 594000000, 99, 1, 2}, + { 13000000, 594000000, 91, 1, 2}, /* actual: 591.5 MHz */ + { 16800000, 594000000, 71, 1, 2}, /* actual: 596.4 MHz */ + { 19200000, 594000000, 62, 1, 2}, /* actual: 595.2 MHz */ + { 26000000, 594000000, 91, 2, 2}, /* actual: 591.5 MHz */ { 0, 0, 0, 0, 0, 0 }, }; @@ -753,21 +761,19 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = { [tegra_clk_rtc] = { .dt_id = TEGRA124_CLK_RTC, .present = true }, [tegra_clk_timer] = { .dt_id = TEGRA124_CLK_TIMER, .present = true }, [tegra_clk_uarta] = { .dt_id = TEGRA124_CLK_UARTA, .present = true }, - [tegra_clk_sdmmc2] = { .dt_id = TEGRA124_CLK_SDMMC2, .present = true }, + [tegra_clk_sdmmc2_8] = { .dt_id = TEGRA124_CLK_SDMMC2, .present = true }, [tegra_clk_i2s1] = { .dt_id = TEGRA124_CLK_I2S1, .present = true }, [tegra_clk_i2c1] = { .dt_id = TEGRA124_CLK_I2C1, .present = true }, [tegra_clk_ndflash] = { .dt_id = TEGRA124_CLK_NDFLASH, .present = true }, - [tegra_clk_sdmmc1] = { .dt_id = TEGRA124_CLK_SDMMC1, .present = true }, - [tegra_clk_sdmmc4] = { .dt_id = TEGRA124_CLK_SDMMC4, .present = true }, + [tegra_clk_sdmmc1_8] = { .dt_id = TEGRA124_CLK_SDMMC1, .present = true }, + [tegra_clk_sdmmc4_8] = { .dt_id = TEGRA124_CLK_SDMMC4, .present = true }, [tegra_clk_pwm] = { .dt_id = TEGRA124_CLK_PWM, .present = true }, [tegra_clk_i2s2] = { .dt_id = TEGRA124_CLK_I2S2, .present = true }, - [tegra_clk_gr2d] = { .dt_id = TEGRA124_CLK_GR_2D, .present = true }, [tegra_clk_usbd] = { .dt_id = TEGRA124_CLK_USBD, .present = true }, [tegra_clk_isp_8] = { .dt_id = TEGRA124_CLK_ISP, .present = true }, - [tegra_clk_gr3d] = { .dt_id = TEGRA124_CLK_GR_3D, .present = true }, [tegra_clk_disp2] = { .dt_id = TEGRA124_CLK_DISP2, .present = true }, [tegra_clk_disp1] = { .dt_id = TEGRA124_CLK_DISP1, .present = true }, - [tegra_clk_host1x] = { .dt_id = TEGRA124_CLK_HOST1X, .present = true }, + [tegra_clk_host1x_8] = { .dt_id = TEGRA124_CLK_HOST1X, .present = true }, [tegra_clk_vcp] = { .dt_id = TEGRA124_CLK_VCP, .present = true }, [tegra_clk_i2s0] = { .dt_id = TEGRA124_CLK_I2S0, .present = true }, [tegra_clk_apbdma] = { .dt_id = TEGRA124_CLK_APBDMA, .present = true }, @@ -794,7 +800,7 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = { [tegra_clk_uartd] = { .dt_id = TEGRA124_CLK_UARTD, .present = true }, [tegra_clk_i2c3] = { .dt_id = TEGRA124_CLK_I2C3, .present = true }, [tegra_clk_sbc4] = { .dt_id = TEGRA124_CLK_SBC4, .present = true }, - [tegra_clk_sdmmc3] = { .dt_id = TEGRA124_CLK_SDMMC3, .present = true }, + [tegra_clk_sdmmc3_8] = { .dt_id = TEGRA124_CLK_SDMMC3, .present = true }, [tegra_clk_pcie] = { .dt_id = TEGRA124_CLK_PCIE, .present = true }, [tegra_clk_owr] = { .dt_id = TEGRA124_CLK_OWR, .present = true }, [tegra_clk_afi] = { .dt_id = TEGRA124_CLK_AFI, .present = true }, @@ -1286,9 +1292,9 @@ static void __init tegra124_pll_init(void __iomem *clk_base, clk_register_clkdev(clk, "pll_d2", NULL); clks[TEGRA124_CLK_PLL_D2] = clk; - /* PLLD2_OUT0 ?? */ + /* PLLD2_OUT0 */ clk = clk_register_fixed_factor(NULL, "pll_d2_out0", "pll_d2", - CLK_SET_RATE_PARENT, 1, 2); + CLK_SET_RATE_PARENT, 1, 1); clk_register_clkdev(clk, "pll_d2_out0", NULL); clks[TEGRA124_CLK_PLL_D2_OUT0] = clk; diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index dbace152b2fa..dace2b1b5ae6 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -574,6 +574,8 @@ static struct tegra_clk tegra20_clks[tegra_clk_max] __initdata = { [tegra_clk_tvdac] = { .dt_id = TEGRA20_CLK_TVDAC, .present = true }, [tegra_clk_vi_sensor] = { .dt_id = TEGRA20_CLK_VI_SENSOR, .present = true }, [tegra_clk_afi] = { .dt_id = TEGRA20_CLK_AFI, .present = true }, + [tegra_clk_fuse] = { .dt_id = TEGRA20_CLK_FUSE, .present = true }, + [tegra_clk_kfuse] = { .dt_id = TEGRA20_CLK_KFUSE, .present = true }, }; static unsigned long tegra20_clk_measure_input_freq(void) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index cd6950fd8caf..52e9329e3c51 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -140,3 +140,51 @@ config VF_PIT_TIMER bool help Support for Period Interrupt Timer on Freescale Vybrid Family SoCs. + +config SYS_SUPPORTS_SH_CMT + bool + +config SYS_SUPPORTS_SH_MTU2 + bool + +config SYS_SUPPORTS_SH_TMU + bool + +config SYS_SUPPORTS_EM_STI + bool + +config SH_TIMER_CMT + bool "Renesas CMT timer driver" if COMPILE_TEST + depends on GENERIC_CLOCKEVENTS + default SYS_SUPPORTS_SH_CMT + help + This enables build of a clocksource and clockevent driver for + the Compare Match Timer (CMT) hardware available in 16/32/48-bit + variants on a wide range of Mobile and Automotive SoCs from Renesas. + +config SH_TIMER_MTU2 + bool "Renesas MTU2 timer driver" if COMPILE_TEST + depends on GENERIC_CLOCKEVENTS + default SYS_SUPPORTS_SH_MTU2 + help + This enables build of a clockevent driver for the Multi-Function + Timer Pulse Unit 2 (TMU2) hardware available on SoCs from Renesas. + This hardware comes with 16 bit-timer registers. + +config SH_TIMER_TMU + bool "Renesas TMU timer driver" if COMPILE_TEST + depends on GENERIC_CLOCKEVENTS + default SYS_SUPPORTS_SH_TMU + help + This enables build of a clocksource and clockevent driver for + the 32-bit Timer Unit (TMU) hardware available on a wide range + SoCs from Renesas. + +config EM_TIMER_STI + bool "Renesas STI timer driver" if COMPILE_TEST + depends on GENERIC_CLOCKEVENTS + default SYS_SUPPORTS_EM_STI + help + This enables build of a clocksource and clockevent driver for + the 48-bit System Timer (STI) hardware available on a SoCs + such as EMEV2 from former NEC Electronics. diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index c7ca50a9c232..aed3488d9426 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_ARCH_MARCO) += timer-marco.o obj-$(CONFIG_ARCH_MOXART) += moxart_timer.o obj-$(CONFIG_ARCH_MXS) += mxs_timer.o obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o +obj-$(CONFIG_ARCH_U300) += timer-u300.o obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o @@ -37,3 +38,4 @@ obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o +obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 95fb944e15ee..57e823c44d2a 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -277,6 +277,7 @@ static void __arch_timer_setup(unsigned type, clk->set_next_event = arch_timer_set_next_event_phys; } } else { + clk->features |= CLOCK_EVT_FEAT_DYNIRQ; clk->name = "arch_mem_timer"; clk->rating = 400; clk->cpumask = cpu_all_mask; diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c index 63f176de0d02..49fbe2847c84 100644 --- a/drivers/clocksource/cadence_ttc_timer.c +++ b/drivers/clocksource/cadence_ttc_timer.c @@ -16,6 +16,7 @@ */ #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/interrupt.h> #include <linux/clockchips.h> #include <linux/of_address.h> @@ -52,6 +53,8 @@ #define TTC_CNT_CNTRL_DISABLE_MASK 0x1 #define TTC_CLK_CNTRL_CSRC_MASK (1 << 5) /* clock source */ +#define TTC_CLK_CNTRL_PSV_MASK 0x1e +#define TTC_CLK_CNTRL_PSV_SHIFT 1 /* * Setup the timers to use pre-scaling, using a fixed value for now that will @@ -63,6 +66,8 @@ #define CLK_CNTRL_PRESCALE_EN 1 #define CNT_CNTRL_RESET (1 << 4) +#define MAX_F_ERR 50 + /** * struct ttc_timer - This definition defines local timer structure * @@ -82,6 +87,8 @@ struct ttc_timer { container_of(x, struct ttc_timer, clk_rate_change_nb) struct ttc_timer_clocksource { + u32 scale_clk_ctrl_reg_old; + u32 scale_clk_ctrl_reg_new; struct ttc_timer ttc; struct clocksource cs; }; @@ -229,32 +236,89 @@ static int ttc_rate_change_clocksource_cb(struct notifier_block *nb, struct ttc_timer_clocksource, ttc); switch (event) { - case POST_RATE_CHANGE: + case PRE_RATE_CHANGE: + { + u32 psv; + unsigned long factor, rate_low, rate_high; + + if (ndata->new_rate > ndata->old_rate) { + factor = DIV_ROUND_CLOSEST(ndata->new_rate, + ndata->old_rate); + rate_low = ndata->old_rate; + rate_high = ndata->new_rate; + } else { + factor = DIV_ROUND_CLOSEST(ndata->old_rate, + ndata->new_rate); + rate_low = ndata->new_rate; + rate_high = ndata->old_rate; + } + + if (!is_power_of_2(factor)) + return NOTIFY_BAD; + + if (abs(rate_high - (factor * rate_low)) > MAX_F_ERR) + return NOTIFY_BAD; + + factor = __ilog2_u32(factor); + /* - * Do whatever is necessary to maintain a proper time base - * - * I cannot find a way to adjust the currently used clocksource - * to the new frequency. __clocksource_updatefreq_hz() sounds - * good, but does not work. Not sure what's that missing. - * - * This approach works, but triggers two clocksource switches. - * The first after unregister to clocksource jiffies. And - * another one after the register to the newly registered timer. - * - * Alternatively we could 'waste' another HW timer to ping pong - * between clock sources. That would also use one register and - * one unregister call, but only trigger one clocksource switch - * for the cost of another HW timer used by the OS. + * store timer clock ctrl register so we can restore it in case + * of an abort. */ - clocksource_unregister(&ttccs->cs); - clocksource_register_hz(&ttccs->cs, - ndata->new_rate / PRESCALE); - /* fall through */ - case PRE_RATE_CHANGE: + ttccs->scale_clk_ctrl_reg_old = + __raw_readl(ttccs->ttc.base_addr + + TTC_CLK_CNTRL_OFFSET); + + psv = (ttccs->scale_clk_ctrl_reg_old & + TTC_CLK_CNTRL_PSV_MASK) >> + TTC_CLK_CNTRL_PSV_SHIFT; + if (ndata->new_rate < ndata->old_rate) + psv -= factor; + else + psv += factor; + + /* prescaler within legal range? */ + if (psv & ~(TTC_CLK_CNTRL_PSV_MASK >> TTC_CLK_CNTRL_PSV_SHIFT)) + return NOTIFY_BAD; + + ttccs->scale_clk_ctrl_reg_new = ttccs->scale_clk_ctrl_reg_old & + ~TTC_CLK_CNTRL_PSV_MASK; + ttccs->scale_clk_ctrl_reg_new |= psv << TTC_CLK_CNTRL_PSV_SHIFT; + + + /* scale down: adjust divider in post-change notification */ + if (ndata->new_rate < ndata->old_rate) + return NOTIFY_DONE; + + /* scale up: adjust divider now - before frequency change */ + __raw_writel(ttccs->scale_clk_ctrl_reg_new, + ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET); + break; + } + case POST_RATE_CHANGE: + /* scale up: pre-change notification did the adjustment */ + if (ndata->new_rate > ndata->old_rate) + return NOTIFY_OK; + + /* scale down: adjust divider now - after frequency change */ + __raw_writel(ttccs->scale_clk_ctrl_reg_new, + ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET); + break; + case ABORT_RATE_CHANGE: + /* we have to undo the adjustment in case we scale up */ + if (ndata->new_rate < ndata->old_rate) + return NOTIFY_OK; + + /* restore original register value */ + __raw_writel(ttccs->scale_clk_ctrl_reg_old, + ttccs->ttc.base_addr + TTC_CLK_CNTRL_OFFSET); + /* fall through */ default: return NOTIFY_DONE; } + + return NOTIFY_DONE; } static void __init ttc_setup_clocksource(struct clk *clk, void __iomem *base) @@ -321,25 +385,12 @@ static int ttc_rate_change_clockevent_cb(struct notifier_block *nb, switch (event) { case POST_RATE_CHANGE: - { - unsigned long flags; - - /* - * clockevents_update_freq should be called with IRQ disabled on - * the CPU the timer provides events for. The timer we use is - * common to both CPUs, not sure if we need to run on both - * cores. - */ - local_irq_save(flags); - clockevents_update_freq(&ttcce->ce, - ndata->new_rate / PRESCALE); - local_irq_restore(flags); - /* update cached frequency */ ttc->freq = ndata->new_rate; + clockevents_update_freq(&ttcce->ce, ndata->new_rate / PRESCALE); + /* fall through */ - } case PRE_RATE_CHANGE: case ABORT_RATE_CHANGE: default: diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 48f76bc05da0..c2e390efbdca 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -410,7 +410,7 @@ static int exynos4_local_timer_setup(struct clock_event_device *evt) mevt = container_of(evt, struct mct_clock_event_device, evt); mevt->base = EXYNOS4_MCT_L_BASE(cpu); - sprintf(mevt->name, "mct_tick%d", cpu); + snprintf(mevt->name, sizeof(mevt->name), "mct_tick%d", cpu); evt->name = mevt->name; evt->cpumask = cpumask_of(cpu); diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c index bf497afba9ad..efb17c3ee120 100644 --- a/drivers/clocksource/sun4i_timer.c +++ b/drivers/clocksource/sun4i_timer.c @@ -196,5 +196,5 @@ static void __init sun4i_timer_init(struct device_node *node) clockevents_config_and_register(&sun4i_clockevent, rate, TIMER_SYNC_TICKS, 0xffffffff); } -CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer", +CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer", sun4i_timer_init); diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c index ee8691b89944..0451e62fac7a 100644 --- a/drivers/clocksource/time-armada-370-xp.c +++ b/drivers/clocksource/time-armada-370-xp.c @@ -85,12 +85,6 @@ static u32 ticks_per_jiffy; static struct clock_event_device __percpu *armada_370_xp_evt; -static void timer_ctrl_clrset(u32 clr, u32 set) -{ - writel((readl(timer_base + TIMER_CTRL_OFF) & ~clr) | set, - timer_base + TIMER_CTRL_OFF); -} - static void local_timer_ctrl_clrset(u32 clr, u32 set) { writel((readl(local_base + TIMER_CTRL_OFF) & ~clr) | set, @@ -245,7 +239,7 @@ static void __init armada_370_xp_timer_common_init(struct device_node *np) clr = TIMER0_25MHZ; enable_mask = TIMER0_EN | TIMER0_DIV(TIMER_DIVIDER_SHIFT); } - timer_ctrl_clrset(clr, set); + atomic_io_modify(timer_base + TIMER_CTRL_OFF, clr | set, set); local_timer_ctrl_clrset(clr, set); /* @@ -263,7 +257,9 @@ static void __init armada_370_xp_timer_common_init(struct device_node *np) writel(0xffffffff, timer_base + TIMER0_VAL_OFF); writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); - timer_ctrl_clrset(0, TIMER0_RELOAD_EN | enable_mask); + atomic_io_modify(timer_base + TIMER_CTRL_OFF, + TIMER0_RELOAD_EN | enable_mask, + TIMER0_RELOAD_EN | enable_mask); /* * Set scale and timer for sched_clock. diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c index 20066222f3f2..0b3ce0399c51 100644 --- a/drivers/clocksource/time-orion.c +++ b/drivers/clocksource/time-orion.c @@ -35,20 +35,6 @@ #define ORION_ONESHOT_MAX 0xfffffffe static void __iomem *timer_base; -static DEFINE_SPINLOCK(timer_ctrl_lock); - -/* - * Thread-safe access to TIMER_CTRL register - * (shared with watchdog timer) - */ -void orion_timer_ctrl_clrset(u32 clr, u32 set) -{ - spin_lock(&timer_ctrl_lock); - writel((readl(timer_base + TIMER_CTRL) & ~clr) | set, - timer_base + TIMER_CTRL); - spin_unlock(&timer_ctrl_lock); -} -EXPORT_SYMBOL(orion_timer_ctrl_clrset); /* * Free-running clocksource handling. @@ -68,7 +54,8 @@ static int orion_clkevt_next_event(unsigned long delta, { /* setup and enable one-shot timer */ writel(delta, timer_base + TIMER1_VAL); - orion_timer_ctrl_clrset(TIMER1_RELOAD_EN, TIMER1_EN); + atomic_io_modify(timer_base + TIMER_CTRL, + TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN); return 0; } @@ -80,10 +67,13 @@ static void orion_clkevt_mode(enum clock_event_mode mode, /* setup and enable periodic timer at 1/HZ intervals */ writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD); writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL); - orion_timer_ctrl_clrset(0, TIMER1_RELOAD_EN | TIMER1_EN); + atomic_io_modify(timer_base + TIMER_CTRL, + TIMER1_RELOAD_EN | TIMER1_EN, + TIMER1_RELOAD_EN | TIMER1_EN); } else { /* disable timer */ - orion_timer_ctrl_clrset(TIMER1_RELOAD_EN | TIMER1_EN, 0); + atomic_io_modify(timer_base + TIMER_CTRL, + TIMER1_RELOAD_EN | TIMER1_EN, 0); } } @@ -131,7 +121,9 @@ static void __init orion_timer_init(struct device_node *np) /* setup timer0 as free-running clocksource */ writel(~0, timer_base + TIMER0_VAL); writel(~0, timer_base + TIMER0_RELOAD); - orion_timer_ctrl_clrset(0, TIMER0_RELOAD_EN | TIMER0_EN); + atomic_io_modify(timer_base + TIMER_CTRL, + TIMER0_RELOAD_EN | TIMER0_EN, + TIMER0_RELOAD_EN | TIMER0_EN); clocksource_mmio_init(timer_base + TIMER0_VAL, "orion_clocksource", clk_get_rate(clk), 300, 32, clocksource_mmio_readl_down); diff --git a/drivers/clocksource/timer-keystone.c b/drivers/clocksource/timer-keystone.c new file mode 100644 index 000000000000..0250354f7e55 --- /dev/null +++ b/drivers/clocksource/timer-keystone.c @@ -0,0 +1,241 @@ +/* + * Keystone broadcast clock-event + * + * Copyright 2013 Texas Instruments, Inc. + * + * Author: Ivan Khoronzhuk <ivan.khoronzhuk@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/clk.h> +#include <linux/clockchips.h> +#include <linux/clocksource.h> +#include <linux/interrupt.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +#define TIMER_NAME "timer-keystone" + +/* Timer register offsets */ +#define TIM12 0x10 +#define TIM34 0x14 +#define PRD12 0x18 +#define PRD34 0x1c +#define TCR 0x20 +#define TGCR 0x24 +#define INTCTLSTAT 0x44 + +/* Timer register bitfields */ +#define TCR_ENAMODE_MASK 0xC0 +#define TCR_ENAMODE_ONESHOT_MASK 0x40 +#define TCR_ENAMODE_PERIODIC_MASK 0x80 + +#define TGCR_TIM_UNRESET_MASK 0x03 +#define INTCTLSTAT_ENINT_MASK 0x01 + +/** + * struct keystone_timer: holds timer's data + * @base: timer memory base address + * @hz_period: cycles per HZ period + * @event_dev: event device based on timer + */ +static struct keystone_timer { + void __iomem *base; + unsigned long hz_period; + struct clock_event_device event_dev; +} timer; + +static inline u32 keystone_timer_readl(unsigned long rg) +{ + return readl_relaxed(timer.base + rg); +} + +static inline void keystone_timer_writel(u32 val, unsigned long rg) +{ + writel_relaxed(val, timer.base + rg); +} + +/** + * keystone_timer_barrier: write memory barrier + * use explicit barrier to avoid using readl/writel non relaxed function + * variants, because in our case non relaxed variants hide the true places + * where barrier is needed. + */ +static inline void keystone_timer_barrier(void) +{ + __iowmb(); +} + +/** + * keystone_timer_config: configures timer to work in oneshot/periodic modes. + * @ mode: mode to configure + * @ period: cycles number to configure for + */ +static int keystone_timer_config(u64 period, enum clock_event_mode mode) +{ + u32 tcr; + u32 off; + + tcr = keystone_timer_readl(TCR); + off = tcr & ~(TCR_ENAMODE_MASK); + + /* set enable mode */ + switch (mode) { + case CLOCK_EVT_MODE_ONESHOT: + tcr |= TCR_ENAMODE_ONESHOT_MASK; + break; + case CLOCK_EVT_MODE_PERIODIC: + tcr |= TCR_ENAMODE_PERIODIC_MASK; + break; + default: + return -1; + } + + /* disable timer */ + keystone_timer_writel(off, TCR); + /* here we have to be sure the timer has been disabled */ + keystone_timer_barrier(); + + /* reset counter to zero, set new period */ + keystone_timer_writel(0, TIM12); + keystone_timer_writel(0, TIM34); + keystone_timer_writel(period & 0xffffffff, PRD12); + keystone_timer_writel(period >> 32, PRD34); + + /* + * enable timer + * here we have to be sure that CNTLO, CNTHI, PRDLO, PRDHI registers + * have been written. + */ + keystone_timer_barrier(); + keystone_timer_writel(tcr, TCR); + return 0; +} + +static void keystone_timer_disable(void) +{ + u32 tcr; + + tcr = keystone_timer_readl(TCR); + + /* disable timer */ + tcr &= ~(TCR_ENAMODE_MASK); + keystone_timer_writel(tcr, TCR); +} + +static irqreturn_t keystone_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + evt->event_handler(evt); + return IRQ_HANDLED; +} + +static int keystone_set_next_event(unsigned long cycles, + struct clock_event_device *evt) +{ + return keystone_timer_config(cycles, evt->mode); +} + +static void keystone_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + keystone_timer_config(timer.hz_period, CLOCK_EVT_MODE_PERIODIC); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_ONESHOT: + keystone_timer_disable(); + break; + default: + break; + } +} + +static void __init keystone_timer_init(struct device_node *np) +{ + struct clock_event_device *event_dev = &timer.event_dev; + unsigned long rate; + struct clk *clk; + int irq, error; + + irq = irq_of_parse_and_map(np, 0); + if (irq == NO_IRQ) { + pr_err("%s: failed to map interrupts\n", __func__); + return; + } + + timer.base = of_iomap(np, 0); + if (!timer.base) { + pr_err("%s: failed to map registers\n", __func__); + return; + } + + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + pr_err("%s: failed to get clock\n", __func__); + iounmap(timer.base); + return; + } + + error = clk_prepare_enable(clk); + if (error) { + pr_err("%s: failed to enable clock\n", __func__); + goto err; + } + + rate = clk_get_rate(clk); + + /* disable, use internal clock source */ + keystone_timer_writel(0, TCR); + /* here we have to be sure the timer has been disabled */ + keystone_timer_barrier(); + + /* reset timer as 64-bit, no pre-scaler, plus features are disabled */ + keystone_timer_writel(0, TGCR); + + /* unreset timer */ + keystone_timer_writel(TGCR_TIM_UNRESET_MASK, TGCR); + + /* init counter to zero */ + keystone_timer_writel(0, TIM12); + keystone_timer_writel(0, TIM34); + + timer.hz_period = DIV_ROUND_UP(rate, HZ); + + /* enable timer interrupts */ + keystone_timer_writel(INTCTLSTAT_ENINT_MASK, INTCTLSTAT); + + error = request_irq(irq, keystone_timer_interrupt, IRQF_TIMER, + TIMER_NAME, event_dev); + if (error) { + pr_err("%s: failed to setup irq\n", __func__); + goto err; + } + + /* setup clockevent */ + event_dev->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + event_dev->set_next_event = keystone_set_next_event; + event_dev->set_mode = keystone_set_mode; + event_dev->cpumask = cpu_all_mask; + event_dev->owner = THIS_MODULE; + event_dev->name = TIMER_NAME; + event_dev->irq = irq; + + clockevents_config_and_register(event_dev, rate, 1, ULONG_MAX); + + pr_info("keystone timer clock @%lu Hz\n", rate); + return; +err: + clk_put(clk); + iounmap(timer.base); +} + +CLOCKSOURCE_OF_DECLARE(keystone_timer, "ti,keystone-timer", + keystone_timer_init); diff --git a/drivers/clocksource/timer-u300.c b/drivers/clocksource/timer-u300.c new file mode 100644 index 000000000000..e63d469661fd --- /dev/null +++ b/drivers/clocksource/timer-u300.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2007-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Timer COH 901 328, runs the OS timer interrupt. + * Author: Linus Walleij <linus.walleij@stericsson.com> + */ +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/timex.h> +#include <linux/clockchips.h> +#include <linux/clocksource.h> +#include <linux/types.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/sched_clock.h> + +/* Generic stuff */ +#include <asm/mach/map.h> +#include <asm/mach/time.h> + +/* + * APP side special timer registers + * This timer contains four timers which can fire an interrupt each. + * OS (operating system) timer @ 32768 Hz + * DD (device driver) timer @ 1 kHz + * GP1 (general purpose 1) timer @ 1MHz + * GP2 (general purpose 2) timer @ 1MHz + */ + +/* Reset OS Timer 32bit (-/W) */ +#define U300_TIMER_APP_ROST (0x0000) +#define U300_TIMER_APP_ROST_TIMER_RESET (0x00000000) +/* Enable OS Timer 32bit (-/W) */ +#define U300_TIMER_APP_EOST (0x0004) +#define U300_TIMER_APP_EOST_TIMER_ENABLE (0x00000000) +/* Disable OS Timer 32bit (-/W) */ +#define U300_TIMER_APP_DOST (0x0008) +#define U300_TIMER_APP_DOST_TIMER_DISABLE (0x00000000) +/* OS Timer Mode Register 32bit (-/W) */ +#define U300_TIMER_APP_SOSTM (0x000c) +#define U300_TIMER_APP_SOSTM_MODE_CONTINUOUS (0x00000000) +#define U300_TIMER_APP_SOSTM_MODE_ONE_SHOT (0x00000001) +/* OS Timer Status Register 32bit (R/-) */ +#define U300_TIMER_APP_OSTS (0x0010) +#define U300_TIMER_APP_OSTS_TIMER_STATE_MASK (0x0000000F) +#define U300_TIMER_APP_OSTS_TIMER_STATE_IDLE (0x00000001) +#define U300_TIMER_APP_OSTS_TIMER_STATE_ACTIVE (0x00000002) +#define U300_TIMER_APP_OSTS_ENABLE_IND (0x00000010) +#define U300_TIMER_APP_OSTS_MODE_MASK (0x00000020) +#define U300_TIMER_APP_OSTS_MODE_CONTINUOUS (0x00000000) +#define U300_TIMER_APP_OSTS_MODE_ONE_SHOT (0x00000020) +#define U300_TIMER_APP_OSTS_IRQ_ENABLED_IND (0x00000040) +#define U300_TIMER_APP_OSTS_IRQ_PENDING_IND (0x00000080) +/* OS Timer Current Count Register 32bit (R/-) */ +#define U300_TIMER_APP_OSTCC (0x0014) +/* OS Timer Terminal Count Register 32bit (R/W) */ +#define U300_TIMER_APP_OSTTC (0x0018) +/* OS Timer Interrupt Enable Register 32bit (-/W) */ +#define U300_TIMER_APP_OSTIE (0x001c) +#define U300_TIMER_APP_OSTIE_IRQ_DISABLE (0x00000000) +#define U300_TIMER_APP_OSTIE_IRQ_ENABLE (0x00000001) +/* OS Timer Interrupt Acknowledge Register 32bit (-/W) */ +#define U300_TIMER_APP_OSTIA (0x0020) +#define U300_TIMER_APP_OSTIA_IRQ_ACK (0x00000080) + +/* Reset DD Timer 32bit (-/W) */ +#define U300_TIMER_APP_RDDT (0x0040) +#define U300_TIMER_APP_RDDT_TIMER_RESET (0x00000000) +/* Enable DD Timer 32bit (-/W) */ +#define U300_TIMER_APP_EDDT (0x0044) +#define U300_TIMER_APP_EDDT_TIMER_ENABLE (0x00000000) +/* Disable DD Timer 32bit (-/W) */ +#define U300_TIMER_APP_DDDT (0x0048) +#define U300_TIMER_APP_DDDT_TIMER_DISABLE (0x00000000) +/* DD Timer Mode Register 32bit (-/W) */ +#define U300_TIMER_APP_SDDTM (0x004c) +#define U300_TIMER_APP_SDDTM_MODE_CONTINUOUS (0x00000000) +#define U300_TIMER_APP_SDDTM_MODE_ONE_SHOT (0x00000001) +/* DD Timer Status Register 32bit (R/-) */ +#define U300_TIMER_APP_DDTS (0x0050) +#define U300_TIMER_APP_DDTS_TIMER_STATE_MASK (0x0000000F) +#define U300_TIMER_APP_DDTS_TIMER_STATE_IDLE (0x00000001) +#define U300_TIMER_APP_DDTS_TIMER_STATE_ACTIVE (0x00000002) +#define U300_TIMER_APP_DDTS_ENABLE_IND (0x00000010) +#define U300_TIMER_APP_DDTS_MODE_MASK (0x00000020) +#define U300_TIMER_APP_DDTS_MODE_CONTINUOUS (0x00000000) +#define U300_TIMER_APP_DDTS_MODE_ONE_SHOT (0x00000020) +#define U300_TIMER_APP_DDTS_IRQ_ENABLED_IND (0x00000040) +#define U300_TIMER_APP_DDTS_IRQ_PENDING_IND (0x00000080) +/* DD Timer Current Count Register 32bit (R/-) */ +#define U300_TIMER_APP_DDTCC (0x0054) +/* DD Timer Terminal Count Register 32bit (R/W) */ +#define U300_TIMER_APP_DDTTC (0x0058) +/* DD Timer Interrupt Enable Register 32bit (-/W) */ +#define U300_TIMER_APP_DDTIE (0x005c) +#define U300_TIMER_APP_DDTIE_IRQ_DISABLE (0x00000000) +#define U300_TIMER_APP_DDTIE_IRQ_ENABLE (0x00000001) +/* DD Timer Interrupt Acknowledge Register 32bit (-/W) */ +#define U300_TIMER_APP_DDTIA (0x0060) +#define U300_TIMER_APP_DDTIA_IRQ_ACK (0x00000080) + +/* Reset GP1 Timer 32bit (-/W) */ +#define U300_TIMER_APP_RGPT1 (0x0080) +#define U300_TIMER_APP_RGPT1_TIMER_RESET (0x00000000) +/* Enable GP1 Timer 32bit (-/W) */ +#define U300_TIMER_APP_EGPT1 (0x0084) +#define U300_TIMER_APP_EGPT1_TIMER_ENABLE (0x00000000) +/* Disable GP1 Timer 32bit (-/W) */ +#define U300_TIMER_APP_DGPT1 (0x0088) +#define U300_TIMER_APP_DGPT1_TIMER_DISABLE (0x00000000) +/* GP1 Timer Mode Register 32bit (-/W) */ +#define U300_TIMER_APP_SGPT1M (0x008c) +#define U300_TIMER_APP_SGPT1M_MODE_CONTINUOUS (0x00000000) +#define U300_TIMER_APP_SGPT1M_MODE_ONE_SHOT (0x00000001) +/* GP1 Timer Status Register 32bit (R/-) */ +#define U300_TIMER_APP_GPT1S (0x0090) +#define U300_TIMER_APP_GPT1S_TIMER_STATE_MASK (0x0000000F) +#define U300_TIMER_APP_GPT1S_TIMER_STATE_IDLE (0x00000001) +#define U300_TIMER_APP_GPT1S_TIMER_STATE_ACTIVE (0x00000002) +#define U300_TIMER_APP_GPT1S_ENABLE_IND (0x00000010) +#define U300_TIMER_APP_GPT1S_MODE_MASK (0x00000020) +#define U300_TIMER_APP_GPT1S_MODE_CONTINUOUS (0x00000000) +#define U300_TIMER_APP_GPT1S_MODE_ONE_SHOT (0x00000020) +#define U300_TIMER_APP_GPT1S_IRQ_ENABLED_IND (0x00000040) +#define U300_TIMER_APP_GPT1S_IRQ_PENDING_IND (0x00000080) +/* GP1 Timer Current Count Register 32bit (R/-) */ +#define U300_TIMER_APP_GPT1CC (0x0094) +/* GP1 Timer Terminal Count Register 32bit (R/W) */ +#define U300_TIMER_APP_GPT1TC (0x0098) +/* GP1 Timer Interrupt Enable Register 32bit (-/W) */ +#define U300_TIMER_APP_GPT1IE (0x009c) +#define U300_TIMER_APP_GPT1IE_IRQ_DISABLE (0x00000000) +#define U300_TIMER_APP_GPT1IE_IRQ_ENABLE (0x00000001) +/* GP1 Timer Interrupt Acknowledge Register 32bit (-/W) */ +#define U300_TIMER_APP_GPT1IA (0x00a0) +#define U300_TIMER_APP_GPT1IA_IRQ_ACK (0x00000080) + +/* Reset GP2 Timer 32bit (-/W) */ +#define U300_TIMER_APP_RGPT2 (0x00c0) +#define U300_TIMER_APP_RGPT2_TIMER_RESET (0x00000000) +/* Enable GP2 Timer 32bit (-/W) */ +#define U300_TIMER_APP_EGPT2 (0x00c4) +#define U300_TIMER_APP_EGPT2_TIMER_ENABLE (0x00000000) +/* Disable GP2 Timer 32bit (-/W) */ +#define U300_TIMER_APP_DGPT2 (0x00c8) +#define U300_TIMER_APP_DGPT2_TIMER_DISABLE (0x00000000) +/* GP2 Timer Mode Register 32bit (-/W) */ +#define U300_TIMER_APP_SGPT2M (0x00cc) +#define U300_TIMER_APP_SGPT2M_MODE_CONTINUOUS (0x00000000) +#define U300_TIMER_APP_SGPT2M_MODE_ONE_SHOT (0x00000001) +/* GP2 Timer Status Register 32bit (R/-) */ +#define U300_TIMER_APP_GPT2S (0x00d0) +#define U300_TIMER_APP_GPT2S_TIMER_STATE_MASK (0x0000000F) +#define U300_TIMER_APP_GPT2S_TIMER_STATE_IDLE (0x00000001) +#define U300_TIMER_APP_GPT2S_TIMER_STATE_ACTIVE (0x00000002) +#define U300_TIMER_APP_GPT2S_ENABLE_IND (0x00000010) +#define U300_TIMER_APP_GPT2S_MODE_MASK (0x00000020) +#define U300_TIMER_APP_GPT2S_MODE_CONTINUOUS (0x00000000) +#define U300_TIMER_APP_GPT2S_MODE_ONE_SHOT (0x00000020) +#define U300_TIMER_APP_GPT2S_IRQ_ENABLED_IND (0x00000040) +#define U300_TIMER_APP_GPT2S_IRQ_PENDING_IND (0x00000080) +/* GP2 Timer Current Count Register 32bit (R/-) */ +#define U300_TIMER_APP_GPT2CC (0x00d4) +/* GP2 Timer Terminal Count Register 32bit (R/W) */ +#define U300_TIMER_APP_GPT2TC (0x00d8) +/* GP2 Timer Interrupt Enable Register 32bit (-/W) */ +#define U300_TIMER_APP_GPT2IE (0x00dc) +#define U300_TIMER_APP_GPT2IE_IRQ_DISABLE (0x00000000) +#define U300_TIMER_APP_GPT2IE_IRQ_ENABLE (0x00000001) +/* GP2 Timer Interrupt Acknowledge Register 32bit (-/W) */ +#define U300_TIMER_APP_GPT2IA (0x00e0) +#define U300_TIMER_APP_GPT2IA_IRQ_ACK (0x00000080) + +/* Clock request control register - all four timers */ +#define U300_TIMER_APP_CRC (0x100) +#define U300_TIMER_APP_CRC_CLOCK_REQUEST_ENABLE (0x00000001) + +static void __iomem *u300_timer_base; + +struct u300_clockevent_data { + struct clock_event_device cevd; + unsigned ticks_per_jiffy; +}; + +/* + * The u300_set_mode() function is always called first, if we + * have oneshot timer active, the oneshot scheduling function + * u300_set_next_event() is called immediately after. + */ +static void u300_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + struct u300_clockevent_data *cevdata = + container_of(evt, struct u300_clockevent_data, cevd); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* Disable interrupts on GPT1 */ + writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE, + u300_timer_base + U300_TIMER_APP_GPT1IE); + /* Disable GP1 while we're reprogramming it. */ + writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE, + u300_timer_base + U300_TIMER_APP_DGPT1); + /* + * Set the periodic mode to a certain number of ticks per + * jiffy. + */ + writel(cevdata->ticks_per_jiffy, + u300_timer_base + U300_TIMER_APP_GPT1TC); + /* + * Set continuous mode, so the timer keeps triggering + * interrupts. + */ + writel(U300_TIMER_APP_SGPT1M_MODE_CONTINUOUS, + u300_timer_base + U300_TIMER_APP_SGPT1M); + /* Enable timer interrupts */ + writel(U300_TIMER_APP_GPT1IE_IRQ_ENABLE, + u300_timer_base + U300_TIMER_APP_GPT1IE); + /* Then enable the OS timer again */ + writel(U300_TIMER_APP_EGPT1_TIMER_ENABLE, + u300_timer_base + U300_TIMER_APP_EGPT1); + break; + case CLOCK_EVT_MODE_ONESHOT: + /* Just break; here? */ + /* + * The actual event will be programmed by the next event hook, + * so we just set a dummy value somewhere at the end of the + * universe here. + */ + /* Disable interrupts on GPT1 */ + writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE, + u300_timer_base + U300_TIMER_APP_GPT1IE); + /* Disable GP1 while we're reprogramming it. */ + writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE, + u300_timer_base + U300_TIMER_APP_DGPT1); + /* + * Expire far in the future, u300_set_next_event() will be + * called soon... + */ + writel(0xFFFFFFFF, u300_timer_base + U300_TIMER_APP_GPT1TC); + /* We run one shot per tick here! */ + writel(U300_TIMER_APP_SGPT1M_MODE_ONE_SHOT, + u300_timer_base + U300_TIMER_APP_SGPT1M); + /* Enable interrupts for this timer */ + writel(U300_TIMER_APP_GPT1IE_IRQ_ENABLE, + u300_timer_base + U300_TIMER_APP_GPT1IE); + /* Enable timer */ + writel(U300_TIMER_APP_EGPT1_TIMER_ENABLE, + u300_timer_base + U300_TIMER_APP_EGPT1); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + /* Disable interrupts on GP1 */ + writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE, + u300_timer_base + U300_TIMER_APP_GPT1IE); + /* Disable GP1 */ + writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE, + u300_timer_base + U300_TIMER_APP_DGPT1); + break; + case CLOCK_EVT_MODE_RESUME: + /* Ignore this call */ + break; + } +} + +/* + * The app timer in one shot mode obviously has to be reprogrammed + * in EXACTLY this sequence to work properly. Do NOT try to e.g. replace + * the interrupt disable + timer disable commands with a reset command, + * it will fail miserably. Apparently (and I found this the hard way) + * the timer is very sensitive to the instruction order, though you don't + * get that impression from the data sheet. + */ +static int u300_set_next_event(unsigned long cycles, + struct clock_event_device *evt) + +{ + /* Disable interrupts on GPT1 */ + writel(U300_TIMER_APP_GPT1IE_IRQ_DISABLE, + u300_timer_base + U300_TIMER_APP_GPT1IE); + /* Disable GP1 while we're reprogramming it. */ + writel(U300_TIMER_APP_DGPT1_TIMER_DISABLE, + u300_timer_base + U300_TIMER_APP_DGPT1); + /* Reset the General Purpose timer 1. */ + writel(U300_TIMER_APP_RGPT1_TIMER_RESET, + u300_timer_base + U300_TIMER_APP_RGPT1); + /* IRQ in n * cycles */ + writel(cycles, u300_timer_base + U300_TIMER_APP_GPT1TC); + /* + * We run one shot per tick here! (This is necessary to reconfigure, + * the timer will tilt if you don't!) + */ + writel(U300_TIMER_APP_SGPT1M_MODE_ONE_SHOT, + u300_timer_base + U300_TIMER_APP_SGPT1M); + /* Enable timer interrupts */ + writel(U300_TIMER_APP_GPT1IE_IRQ_ENABLE, + u300_timer_base + U300_TIMER_APP_GPT1IE); + /* Then enable the OS timer again */ + writel(U300_TIMER_APP_EGPT1_TIMER_ENABLE, + u300_timer_base + U300_TIMER_APP_EGPT1); + return 0; +} + +static struct u300_clockevent_data u300_clockevent_data = { + /* Use general purpose timer 1 as clock event */ + .cevd = { + .name = "GPT1", + /* Reasonably fast and accurate clock event */ + .rating = 300, + .features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = u300_set_next_event, + .set_mode = u300_set_mode, + }, +}; + +/* Clock event timer interrupt handler */ +static irqreturn_t u300_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = &u300_clockevent_data.cevd; + /* ACK/Clear timer IRQ for the APP GPT1 Timer */ + + writel(U300_TIMER_APP_GPT1IA_IRQ_ACK, + u300_timer_base + U300_TIMER_APP_GPT1IA); + evt->event_handler(evt); + return IRQ_HANDLED; +} + +static struct irqaction u300_timer_irq = { + .name = "U300 Timer Tick", + .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = u300_timer_interrupt, +}; + +/* + * Override the global weak sched_clock symbol with this + * local implementation which uses the clocksource to get some + * better resolution when scheduling the kernel. We accept that + * this wraps around for now, since it is just a relative time + * stamp. (Inspired by OMAP implementation.) + */ + +static u64 notrace u300_read_sched_clock(void) +{ + return readl(u300_timer_base + U300_TIMER_APP_GPT2CC); +} + +static unsigned long u300_read_current_timer(void) +{ + return readl(u300_timer_base + U300_TIMER_APP_GPT2CC); +} + +static struct delay_timer u300_delay_timer; + +/* + * This sets up the system timers, clock source and clock event. + */ +static void __init u300_timer_init_of(struct device_node *np) +{ + unsigned int irq; + struct clk *clk; + unsigned long rate; + + u300_timer_base = of_iomap(np, 0); + if (!u300_timer_base) + panic("could not ioremap system timer\n"); + + /* Get the IRQ for the GP1 timer */ + irq = irq_of_parse_and_map(np, 2); + if (!irq) + panic("no IRQ for system timer\n"); + + pr_info("U300 GP1 timer @ base: %p, IRQ: %u\n", u300_timer_base, irq); + + /* Clock the interrupt controller */ + clk = of_clk_get(np, 0); + BUG_ON(IS_ERR(clk)); + clk_prepare_enable(clk); + rate = clk_get_rate(clk); + + u300_clockevent_data.ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ); + + sched_clock_register(u300_read_sched_clock, 32, rate); + + u300_delay_timer.read_current_timer = &u300_read_current_timer; + u300_delay_timer.freq = rate; + register_current_timer_delay(&u300_delay_timer); + + /* + * Disable the "OS" and "DD" timers - these are designed for Symbian! + * Example usage in cnh1601578 cpu subsystem pd_timer_app.c + */ + writel(U300_TIMER_APP_CRC_CLOCK_REQUEST_ENABLE, + u300_timer_base + U300_TIMER_APP_CRC); + writel(U300_TIMER_APP_ROST_TIMER_RESET, + u300_timer_base + U300_TIMER_APP_ROST); + writel(U300_TIMER_APP_DOST_TIMER_DISABLE, + u300_timer_base + U300_TIMER_APP_DOST); + writel(U300_TIMER_APP_RDDT_TIMER_RESET, + u300_timer_base + U300_TIMER_APP_RDDT); + writel(U300_TIMER_APP_DDDT_TIMER_DISABLE, + u300_timer_base + U300_TIMER_APP_DDDT); + + /* Reset the General Purpose timer 1. */ + writel(U300_TIMER_APP_RGPT1_TIMER_RESET, + u300_timer_base + U300_TIMER_APP_RGPT1); + + /* Set up the IRQ handler */ + setup_irq(irq, &u300_timer_irq); + + /* Reset the General Purpose timer 2 */ + writel(U300_TIMER_APP_RGPT2_TIMER_RESET, + u300_timer_base + U300_TIMER_APP_RGPT2); + /* Set this timer to run around forever */ + writel(0xFFFFFFFFU, u300_timer_base + U300_TIMER_APP_GPT2TC); + /* Set continuous mode so it wraps around */ + writel(U300_TIMER_APP_SGPT2M_MODE_CONTINUOUS, + u300_timer_base + U300_TIMER_APP_SGPT2M); + /* Disable timer interrupts */ + writel(U300_TIMER_APP_GPT2IE_IRQ_DISABLE, + u300_timer_base + U300_TIMER_APP_GPT2IE); + /* Then enable the GP2 timer to use as a free running us counter */ + writel(U300_TIMER_APP_EGPT2_TIMER_ENABLE, + u300_timer_base + U300_TIMER_APP_EGPT2); + + /* Use general purpose timer 2 as clock source */ + if (clocksource_mmio_init(u300_timer_base + U300_TIMER_APP_GPT2CC, + "GPT2", rate, 300, 32, clocksource_mmio_readl_up)) + pr_err("timer: failed to initialize U300 clock source\n"); + + /* Configure and register the clockevent */ + clockevents_config_and_register(&u300_clockevent_data.cevd, rate, + 1, 0xffffffff); + + /* + * TODO: init and register the rest of the timers too, they can be + * used by hrtimers! + */ +} + +CLOCKSOURCE_OF_DECLARE(u300_timer, "stericsson,u300-apptimer", + u300_timer_init_of); diff --git a/drivers/clocksource/vf_pit_timer.c b/drivers/clocksource/vf_pit_timer.c index 02821b06a39e..a918bc481c52 100644 --- a/drivers/clocksource/vf_pit_timer.c +++ b/drivers/clocksource/vf_pit_timer.c @@ -54,7 +54,7 @@ static inline void pit_irq_acknowledge(void) static u64 pit_read_sched_clock(void) { - return __raw_readl(clksrc_base + PITCVAL); + return ~__raw_readl(clksrc_base + PITCVAL); } static int __init pit_clocksource_init(unsigned long rate) diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c index 18c5b9b16645..148d707a1d43 100644 --- a/drivers/connector/cn_proc.c +++ b/drivers/connector/cn_proc.c @@ -95,7 +95,7 @@ void proc_fork_connector(struct task_struct *task) msg->len = sizeof(*ev); msg->flags = 0; /* not used */ /* If cn_netlink_send() failed, the data is not sent */ - cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); + cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); } void proc_exec_connector(struct task_struct *task) @@ -122,7 +122,7 @@ void proc_exec_connector(struct task_struct *task) msg->ack = 0; /* not used */ msg->len = sizeof(*ev); msg->flags = 0; /* not used */ - cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); + cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); } void proc_id_connector(struct task_struct *task, int which_id) @@ -163,7 +163,7 @@ void proc_id_connector(struct task_struct *task, int which_id) msg->ack = 0; /* not used */ msg->len = sizeof(*ev); msg->flags = 0; /* not used */ - cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); + cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); } void proc_sid_connector(struct task_struct *task) @@ -190,7 +190,7 @@ void proc_sid_connector(struct task_struct *task) msg->ack = 0; /* not used */ msg->len = sizeof(*ev); msg->flags = 0; /* not used */ - cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); + cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); } void proc_ptrace_connector(struct task_struct *task, int ptrace_id) @@ -225,7 +225,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id) msg->ack = 0; /* not used */ msg->len = sizeof(*ev); msg->flags = 0; /* not used */ - cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); + cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); } void proc_comm_connector(struct task_struct *task) @@ -253,7 +253,7 @@ void proc_comm_connector(struct task_struct *task) msg->ack = 0; /* not used */ msg->len = sizeof(*ev); msg->flags = 0; /* not used */ - cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); + cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); } void proc_coredump_connector(struct task_struct *task) @@ -280,7 +280,7 @@ void proc_coredump_connector(struct task_struct *task) msg->ack = 0; /* not used */ msg->len = sizeof(*ev); msg->flags = 0; /* not used */ - cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); + cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); } void proc_exit_connector(struct task_struct *task) @@ -309,7 +309,7 @@ void proc_exit_connector(struct task_struct *task) msg->ack = 0; /* not used */ msg->len = sizeof(*ev); msg->flags = 0; /* not used */ - cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); + cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); } /* @@ -343,7 +343,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack) msg->ack = rcvd_ack + 1; msg->len = sizeof(*ev); msg->flags = 0; /* not used */ - cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL); + cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL); } /** diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index a36749f1e44a..77afe7487d34 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -50,7 +50,7 @@ static int cn_already_initialized; * * Sequence number is incremented with each message to be sent. * - * If we expect reply to our message then the sequence number in + * If we expect a reply to our message then the sequence number in * received message MUST be the same as in original message, and * acknowledge number MUST be the same + 1. * @@ -62,8 +62,11 @@ static int cn_already_initialized; * the acknowledgement number in the original message + 1, then it is * a new message. * + * The message is sent to, the portid if given, the group if given, both if + * both, or if both are zero then the group is looked up and sent there. */ -int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask) +int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group, + gfp_t gfp_mask) { struct cn_callback_entry *__cbq; unsigned int size; @@ -74,7 +77,9 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask) u32 group = 0; int found = 0; - if (!__group) { + if (portid || __group) { + group = __group; + } else { spin_lock_bh(&dev->cbdev->queue_lock); list_for_each_entry(__cbq, &dev->cbdev->queue_list, callback_entry) { @@ -88,11 +93,9 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask) if (!found) return -ENODEV; - } else { - group = __group; } - if (!netlink_has_listeners(dev->nls, group)) + if (!portid && !netlink_has_listeners(dev->nls, group)) return -ESRCH; size = sizeof(*msg) + msg->len; @@ -113,7 +116,10 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask) NETLINK_CB(skb).dst_group = group; - return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask); + if (group) + return netlink_broadcast(dev->nls, skb, portid, group, + gfp_mask); + return netlink_unicast(dev->nls, skb, portid, !(gfp_mask&__GFP_WAIT)); } EXPORT_SYMBOL_GPL(cn_netlink_send); diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 4b029c0944af..1fbe11f2a146 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -200,7 +200,7 @@ source "drivers/cpufreq/Kconfig.x86" endmenu menu "ARM CPU frequency scaling drivers" -depends on ARM +depends on ARM || ARM64 source "drivers/cpufreq/Kconfig.arm" endmenu diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 31297499a60a..9fb627046e17 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -2,6 +2,7 @@ # ARM CPU Frequency scaling drivers # +# big LITTLE core layer and glue drivers config ARM_BIG_LITTLE_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver" depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK @@ -16,6 +17,14 @@ config ARM_DT_BL_CPUFREQ This enables probing via DT for Generic CPUfreq driver for ARM big.LITTLE platform. This gets frequency tables from DT. +config ARM_VEXPRESS_SPC_CPUFREQ + tristate "Versatile Express SPC based CPUfreq driver" + depends on ARM_BIG_LITTLE_CPUFREQ && ARCH_VEXPRESS_SPC + help + This add the CPUfreq driver support for Versatile Express + big.LITTLE platforms using SPC for power management. + + config ARM_EXYNOS_CPUFREQ bool @@ -241,11 +250,3 @@ config ARM_TEGRA_CPUFREQ default y help This adds the CPUFreq driver support for TEGRA SOCs. - -config ARM_VEXPRESS_SPC_CPUFREQ - tristate "Versatile Express SPC based CPUfreq driver" - select ARM_BIG_LITTLE_CPUFREQ - depends on ARCH_VEXPRESS_SPC - help - This add the CPUfreq driver support for Versatile Express - big.LITTLE platforms using SPC for power management. diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 18448a7e9f86..822ca03a87f7 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -855,7 +855,6 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy) pr_debug("acpi_cpufreq_cpu_exit\n"); if (data) { - cpufreq_frequency_table_put_attr(policy->cpu); per_cpu(acfreq_data, policy->cpu) = NULL; acpi_processor_unregister_performance(data->acpi_data, policy->cpu); diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index 72f87e9317e3..bad2ed317ba2 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -446,9 +446,12 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy) } if (cur_cluster < MAX_CLUSTERS) { + int cpu; + cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); - per_cpu(physical_cluster, policy->cpu) = cur_cluster; + for_each_cpu(cpu, policy->cpus) + per_cpu(physical_cluster, cpu) = cur_cluster; } else { /* Assumption: during init, we are always running on A15 */ per_cpu(physical_cluster, policy->cpu) = A15_CLUSTER; @@ -478,7 +481,6 @@ static int bL_cpufreq_exit(struct cpufreq_policy *policy) return -ENODEV; } - cpufreq_frequency_table_put_attr(policy->cpu); put_cluster_clk_and_freq_table(cpu_dev); dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu); diff --git a/drivers/cpufreq/blackfin-cpufreq.c b/drivers/cpufreq/blackfin-cpufreq.c index e9e63fc9c2c9..a9f8e5bd0716 100644 --- a/drivers/cpufreq/blackfin-cpufreq.c +++ b/drivers/cpufreq/blackfin-cpufreq.c @@ -195,7 +195,6 @@ static struct cpufreq_driver bfin_driver = { .target_index = bfin_target, .get = bfin_getfreq_khz, .init = __bfin_cpu_init, - .exit = cpufreq_generic_exit, .name = "bfin cpufreq", .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index 0c12ffc0ebcb..1bf6bbac3e03 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -109,7 +109,6 @@ static struct cpufreq_driver cpu0_cpufreq_driver = { .target_index = cpu0_set_target, .get = cpufreq_generic_get, .init = cpu0_cpufreq_init, - .exit = cpufreq_generic_exit, .name = "generic_cpu0", .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index cb003a6b72c8..3aa7a7a226b3 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -26,7 +26,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/syscore_ops.h> +#include <linux/suspend.h> #include <linux/tick.h> #include <trace/events/power.h> @@ -42,10 +42,11 @@ static DEFINE_RWLOCK(cpufreq_driver_lock); DEFINE_MUTEX(cpufreq_governor_lock); static LIST_HEAD(cpufreq_policy_list); -#ifdef CONFIG_HOTPLUG_CPU /* This one keeps track of the previously set governor of a removed CPU */ static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor); -#endif + +/* Flag to suspend/resume CPUFreq governors */ +static bool cpufreq_suspended; static inline bool has_target(void) { @@ -181,8 +182,8 @@ unsigned int cpufreq_generic_get(unsigned int cpu) struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); if (!policy || IS_ERR(policy->clk)) { - pr_err("%s: No %s associated to cpu: %d\n", __func__, - policy ? "clk" : "policy", cpu); + pr_err("%s: No %s associated to cpu: %d\n", + __func__, policy ? "clk" : "policy", cpu); return 0; } @@ -190,6 +191,12 @@ unsigned int cpufreq_generic_get(unsigned int cpu) } EXPORT_SYMBOL_GPL(cpufreq_generic_get); +/* Only for cpufreq core internal use */ +struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu) +{ + return per_cpu(cpufreq_cpu_data, cpu); +} + struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) { struct cpufreq_policy *policy = NULL; @@ -254,15 +261,14 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) if (!l_p_j_ref_freq) { l_p_j_ref = loops_per_jiffy; l_p_j_ref_freq = ci->old; - pr_debug("saving %lu as reference value for loops_per_jiffy; " - "freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq); + pr_debug("saving %lu as reference value for loops_per_jiffy; freq is %u kHz\n", + l_p_j_ref, l_p_j_ref_freq); } - if ((val == CPUFREQ_POSTCHANGE && ci->old != ci->new) || - (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) { + if (val == CPUFREQ_POSTCHANGE && ci->old != ci->new) { loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); - pr_debug("scaling loops_per_jiffy to %lu " - "for frequency %u kHz\n", loops_per_jiffy, ci->new); + pr_debug("scaling loops_per_jiffy to %lu for frequency %u kHz\n", + loops_per_jiffy, ci->new); } } #else @@ -282,7 +288,7 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, freqs->flags = cpufreq_driver->flags; pr_debug("notification %u of frequency transition to %u kHz\n", - state, freqs->new); + state, freqs->new); switch (state) { @@ -294,9 +300,8 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) { if ((policy) && (policy->cpu == freqs->cpu) && (policy->cur) && (policy->cur != freqs->old)) { - pr_debug("Warning: CPU frequency is" - " %u, cpufreq assumed %u kHz.\n", - freqs->old, policy->cur); + pr_debug("Warning: CPU frequency is %u, cpufreq assumed %u kHz\n", + freqs->old, policy->cur); freqs->old = policy->cur; } } @@ -307,8 +312,8 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, case CPUFREQ_POSTCHANGE: adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); - pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new, - (unsigned long)freqs->cpu); + pr_debug("FREQ: %lu - CPU: %lu\n", + (unsigned long)freqs->new, (unsigned long)freqs->cpu); trace_cpu_frequency(freqs->new, freqs->cpu); srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); @@ -352,7 +357,7 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_post_transition); /********************************************************************* * SYSFS INTERFACE * *********************************************************************/ -ssize_t show_boost(struct kobject *kobj, +static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, char *buf) { return sprintf(buf, "%d\n", cpufreq_driver->boost_enabled); @@ -368,13 +373,13 @@ static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, return -EINVAL; if (cpufreq_boost_trigger_state(enable)) { - pr_err("%s: Cannot %s BOOST!\n", __func__, - enable ? "enable" : "disable"); + pr_err("%s: Cannot %s BOOST!\n", + __func__, enable ? "enable" : "disable"); return -EINVAL; } - pr_debug("%s: cpufreq BOOST %s\n", __func__, - enable ? "enabled" : "disabled"); + pr_debug("%s: cpufreq BOOST %s\n", + __func__, enable ? "enabled" : "disabled"); return count; } @@ -879,18 +884,25 @@ err_out_kobj_put: static void cpufreq_init_policy(struct cpufreq_policy *policy) { + struct cpufreq_governor *gov = NULL; struct cpufreq_policy new_policy; int ret = 0; 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)); + if (gov) + pr_debug("Restoring governor %s for cpu %d\n", + policy->governor->name, policy->cpu); + else + gov = CPUFREQ_DEFAULT_GOVERNOR; + + new_policy.governor = gov; + /* Use the default policy if its valid. */ if (cpufreq_driver->setpolicy) - cpufreq_parse_governor(policy->governor->name, - &new_policy.policy, NULL); - - /* assure that the starting sequence is run in cpufreq_set_policy */ - policy->governor = NULL; + cpufreq_parse_governor(gov->name, &new_policy.policy, NULL); /* set default policy */ ret = cpufreq_set_policy(policy, &new_policy); @@ -927,8 +939,11 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, up_write(&policy->rwsem); if (has_target()) { - if ((ret = __cpufreq_governor(policy, CPUFREQ_GOV_START)) || - (ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))) { + ret = __cpufreq_governor(policy, CPUFREQ_GOV_START); + if (!ret) + ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); + + if (ret) { pr_err("%s: Failed to start governor\n", __func__); return ret; } @@ -949,6 +964,8 @@ static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu) read_unlock_irqrestore(&cpufreq_driver_lock, flags); + policy->governor = NULL; + return policy; } @@ -1022,21 +1039,19 @@ static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu) up_write(&policy->rwsem); - cpufreq_frequency_table_update_policy_cpu(policy); blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_UPDATE_POLICY_CPU, policy); } -static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, - bool frozen) +static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) { unsigned int j, cpu = dev->id; int ret = -ENOMEM; struct cpufreq_policy *policy; unsigned long flags; + bool recover_policy = cpufreq_suspended; #ifdef CONFIG_HOTPLUG_CPU struct cpufreq_policy *tpolicy; - struct cpufreq_governor *gov; #endif if (cpu_is_offline(cpu)) @@ -1075,9 +1090,9 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, * Restore the saved policy when doing light-weight init and fall back * to the full init if that fails. */ - policy = frozen ? cpufreq_policy_restore(cpu) : NULL; + policy = recover_policy ? cpufreq_policy_restore(cpu) : NULL; if (!policy) { - frozen = false; + recover_policy = false; policy = cpufreq_policy_alloc(); if (!policy) goto nomem_out; @@ -1089,12 +1104,11 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, * the creation of a brand new one. So we need to perform this update * by invoking update_policy_cpu(). */ - if (frozen && cpu != policy->cpu) + if (recover_policy && cpu != policy->cpu) update_policy_cpu(policy, cpu); else policy->cpu = cpu; - policy->governor = CPUFREQ_DEFAULT_GOVERNOR; cpumask_copy(policy->cpus, cpumask_of(cpu)); init_completion(&policy->kobj_unregister); @@ -1109,12 +1123,27 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, goto err_set_policy_cpu; } + /* related cpus should atleast have policy->cpus */ + cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus); + + /* + * affected cpus must always be the one, which are online. We aren't + * managing offline cpus here. + */ + cpumask_and(policy->cpus, policy->cpus, cpu_online_mask); + + if (!recover_policy) { + policy->user_policy.min = policy->min; + policy->user_policy.max = policy->max; + } + + down_write(&policy->rwsem); write_lock_irqsave(&cpufreq_driver_lock, flags); for_each_cpu(j, policy->cpus) per_cpu(cpufreq_cpu_data, j) = policy; write_unlock_irqrestore(&cpufreq_driver_lock, flags); - if (cpufreq_driver->get) { + if (cpufreq_driver->get && !cpufreq_driver->setpolicy) { policy->cur = cpufreq_driver->get(policy->cpu); if (!policy->cur) { pr_err("%s: ->get() failed\n", __func__); @@ -1162,33 +1191,10 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, } } - /* related cpus should atleast have policy->cpus */ - cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus); - - /* - * affected cpus must always be the one, which are online. We aren't - * managing offline cpus here. - */ - cpumask_and(policy->cpus, policy->cpus, cpu_online_mask); - - if (!frozen) { - policy->user_policy.min = policy->min; - policy->user_policy.max = policy->max; - } - blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_START, policy); -#ifdef CONFIG_HOTPLUG_CPU - gov = __find_governor(per_cpu(cpufreq_cpu_governor, cpu)); - if (gov) { - policy->governor = gov; - pr_debug("Restoring governor %s for cpu %d\n", - policy->governor->name, cpu); - } -#endif - - if (!frozen) { + if (!recover_policy) { ret = cpufreq_add_dev_interface(policy, dev); if (ret) goto err_out_unregister; @@ -1202,10 +1208,11 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, cpufreq_init_policy(policy); - if (!frozen) { + if (!recover_policy) { policy->user_policy.policy = policy->policy; policy->user_policy.governor = policy->governor; } + up_write(&policy->rwsem); kobject_uevent(&policy->kobj, KOBJ_ADD); up_read(&cpufreq_rwsem); @@ -1224,7 +1231,7 @@ err_get_freq: if (cpufreq_driver->exit) cpufreq_driver->exit(policy); err_set_policy_cpu: - if (frozen) { + if (recover_policy) { /* Do not leave stale fallback data behind. */ per_cpu(cpufreq_cpu_data_fallback, cpu) = NULL; cpufreq_policy_put_kobj(policy); @@ -1248,7 +1255,7 @@ nomem_out: */ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) { - return __cpufreq_add_dev(dev, sif, false); + return __cpufreq_add_dev(dev, sif); } static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy, @@ -1263,7 +1270,7 @@ static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy, sysfs_remove_link(&cpu_dev->kobj, "cpufreq"); ret = kobject_move(&policy->kobj, &cpu_dev->kobj); if (ret) { - pr_err("%s: Failed to move kobj: %d", __func__, ret); + pr_err("%s: Failed to move kobj: %d\n", __func__, ret); down_write(&policy->rwsem); cpumask_set_cpu(old_cpu, policy->cpus); @@ -1279,8 +1286,7 @@ static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy, } static int __cpufreq_remove_dev_prepare(struct device *dev, - struct subsys_interface *sif, - bool frozen) + struct subsys_interface *sif) { unsigned int cpu = dev->id, cpus; int new_cpu, ret; @@ -1294,7 +1300,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, policy = per_cpu(cpufreq_cpu_data, cpu); /* Save the policy somewhere when doing a light-weight tear-down */ - if (frozen) + if (cpufreq_suspended) per_cpu(cpufreq_cpu_data_fallback, cpu) = policy; write_unlock_irqrestore(&cpufreq_driver_lock, flags); @@ -1312,11 +1318,9 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, } } -#ifdef CONFIG_HOTPLUG_CPU if (!cpufreq_driver->setpolicy) strncpy(per_cpu(cpufreq_cpu_governor, cpu), policy->governor->name, CPUFREQ_NAME_LEN); -#endif down_read(&policy->rwsem); cpus = cpumask_weight(policy->cpus); @@ -1329,19 +1333,19 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, if (new_cpu >= 0) { update_policy_cpu(policy, new_cpu); - if (!frozen) { + if (!cpufreq_suspended) pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n", - __func__, new_cpu, cpu); - } + __func__, new_cpu, cpu); } + } else if (cpufreq_driver->stop_cpu && cpufreq_driver->setpolicy) { + cpufreq_driver->stop_cpu(policy); } return 0; } static int __cpufreq_remove_dev_finish(struct device *dev, - struct subsys_interface *sif, - bool frozen) + struct subsys_interface *sif) { unsigned int cpu = dev->id, cpus; int ret; @@ -1371,12 +1375,12 @@ static int __cpufreq_remove_dev_finish(struct device *dev, CPUFREQ_GOV_POLICY_EXIT); if (ret) { pr_err("%s: Failed to exit governor\n", - __func__); + __func__); return ret; } } - if (!frozen) + if (!cpufreq_suspended) cpufreq_policy_put_kobj(policy); /* @@ -1392,16 +1396,16 @@ static int __cpufreq_remove_dev_finish(struct device *dev, list_del(&policy->policy_list); write_unlock_irqrestore(&cpufreq_driver_lock, flags); - if (!frozen) + if (!cpufreq_suspended) cpufreq_policy_free(policy); - } else { - if (has_target()) { - if ((ret = __cpufreq_governor(policy, CPUFREQ_GOV_START)) || - (ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))) { - pr_err("%s: Failed to start governor\n", - __func__); - return ret; - } + } else if (has_target()) { + ret = __cpufreq_governor(policy, CPUFREQ_GOV_START); + if (!ret) + ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); + + if (ret) { + pr_err("%s: Failed to start governor\n", __func__); + return ret; } } @@ -1422,10 +1426,10 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) if (cpu_is_offline(cpu)) return 0; - ret = __cpufreq_remove_dev_prepare(dev, sif, false); + ret = __cpufreq_remove_dev_prepare(dev, sif); if (!ret) - ret = __cpufreq_remove_dev_finish(dev, sif, false); + ret = __cpufreq_remove_dev_finish(dev, sif); return ret; } @@ -1456,8 +1460,8 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, 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); + pr_debug("Warning: CPU frequency out of sync: cpufreq and timing core thinks of %u, is %u kHz\n", + old_freq, new_freq); freqs.old = old_freq; freqs.new = new_freq; @@ -1546,23 +1550,16 @@ static unsigned int __cpufreq_get(unsigned int cpu) */ unsigned int cpufreq_get(unsigned int cpu) { - struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); unsigned int ret_freq = 0; - if (cpufreq_disabled() || !cpufreq_driver) - return -ENOENT; - - BUG_ON(!policy); - - if (!down_read_trylock(&cpufreq_rwsem)) - return 0; - - down_read(&policy->rwsem); - - ret_freq = __cpufreq_get(cpu); + if (policy) { + down_read(&policy->rwsem); + ret_freq = __cpufreq_get(cpu); + up_read(&policy->rwsem); - up_read(&policy->rwsem); - up_read(&cpufreq_rwsem); + cpufreq_cpu_put(policy); + } return ret_freq; } @@ -1575,83 +1572,104 @@ static struct subsys_interface cpufreq_interface = { .remove_dev = cpufreq_remove_dev, }; +/* + * In case platform wants some specific frequency to be configured + * during suspend.. + */ +int cpufreq_generic_suspend(struct cpufreq_policy *policy) +{ + int ret; + + if (!policy->suspend_freq) { + pr_err("%s: suspend_freq can't be zero\n", __func__); + return -EINVAL; + } + + pr_debug("%s: Setting suspend-freq: %u\n", __func__, + policy->suspend_freq); + + ret = __cpufreq_driver_target(policy, policy->suspend_freq, + CPUFREQ_RELATION_H); + if (ret) + pr_err("%s: unable to set suspend-freq: %u. err: %d\n", + __func__, policy->suspend_freq, ret); + + return ret; +} +EXPORT_SYMBOL(cpufreq_generic_suspend); + /** - * cpufreq_bp_suspend - Prepare the boot CPU for system suspend. + * cpufreq_suspend() - Suspend CPUFreq governors * - * This function is only executed for the boot processor. The other CPUs - * have been put offline by means of CPU hotplug. + * Called during system wide Suspend/Hibernate cycles for suspending governors + * as some platforms can't change frequency after this point in suspend cycle. + * Because some of the devices (like: i2c, regulators, etc) they use for + * changing frequency are suspended quickly after this point. */ -static int cpufreq_bp_suspend(void) +void cpufreq_suspend(void) { - int ret = 0; - - int cpu = smp_processor_id(); struct cpufreq_policy *policy; - pr_debug("suspending cpu %u\n", cpu); + if (!cpufreq_driver) + return; - /* If there's no policy for the boot CPU, we have nothing to do. */ - policy = cpufreq_cpu_get(cpu); - if (!policy) - return 0; + if (!has_target()) + return; - if (cpufreq_driver->suspend) { - ret = cpufreq_driver->suspend(policy); - if (ret) - printk(KERN_ERR "cpufreq: suspend failed in ->suspend " - "step on CPU %u\n", policy->cpu); + pr_debug("%s: Suspending Governors\n", __func__); + + list_for_each_entry(policy, &cpufreq_policy_list, policy_list) { + if (__cpufreq_governor(policy, CPUFREQ_GOV_STOP)) + pr_err("%s: Failed to stop governor for policy: %p\n", + __func__, policy); + else if (cpufreq_driver->suspend + && cpufreq_driver->suspend(policy)) + pr_err("%s: Failed to suspend driver: %p\n", __func__, + policy); } - cpufreq_cpu_put(policy); - return ret; + cpufreq_suspended = true; } /** - * cpufreq_bp_resume - Restore proper frequency handling of the boot CPU. - * - * 1.) resume CPUfreq hardware support (cpufreq_driver->resume()) - * 2.) schedule call cpufreq_update_policy() ASAP as interrupts are - * restored. It will verify that the current freq is in sync with - * what we believe it to be. This is a bit later than when it - * should be, but nonethteless it's better than calling - * cpufreq_driver->get() here which might re-enable interrupts... + * cpufreq_resume() - Resume CPUFreq governors * - * This function is only executed for the boot CPU. The other CPUs have not - * been turned on yet. + * Called during system wide Suspend/Hibernate cycle for resuming governors that + * are suspended with cpufreq_suspend(). */ -static void cpufreq_bp_resume(void) +void cpufreq_resume(void) { - int ret = 0; - - int cpu = smp_processor_id(); struct cpufreq_policy *policy; - pr_debug("resuming cpu %u\n", cpu); + if (!cpufreq_driver) + return; - /* If there's no policy for the boot CPU, we have nothing to do. */ - policy = cpufreq_cpu_get(cpu); - if (!policy) + if (!has_target()) return; - if (cpufreq_driver->resume) { - ret = cpufreq_driver->resume(policy); - if (ret) { - printk(KERN_ERR "cpufreq: resume failed in ->resume " - "step on CPU %u\n", policy->cpu); - goto fail; - } - } + pr_debug("%s: Resuming Governors\n", __func__); - schedule_work(&policy->update); + cpufreq_suspended = false; -fail: - cpufreq_cpu_put(policy); -} + list_for_each_entry(policy, &cpufreq_policy_list, policy_list) { + if (__cpufreq_governor(policy, CPUFREQ_GOV_START) + || __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS)) + pr_err("%s: Failed to start governor for policy: %p\n", + __func__, policy); + else if (cpufreq_driver->resume + && cpufreq_driver->resume(policy)) + pr_err("%s: Failed to resume driver: %p\n", __func__, + policy); -static struct syscore_ops cpufreq_syscore_ops = { - .suspend = cpufreq_bp_suspend, - .resume = cpufreq_bp_resume, -}; + /* + * schedule call cpufreq_update_policy() for boot CPU, i.e. last + * policy in list. It will verify that the current freq is in + * sync with what we believe it to be. + */ + if (list_is_last(&policy->policy_list, &cpufreq_policy_list)) + schedule_work(&policy->update); + } +} /** * cpufreq_get_current_driver - return current driver's name @@ -1767,7 +1785,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, target_freq = policy->min; pr_debug("target for CPU %u: %u kHz, relation %u, requested %u kHz\n", - policy->cpu, target_freq, relation, old_target_freq); + policy->cpu, target_freq, relation, old_target_freq); /* * This might look like a redundant call as we are checking it again @@ -1812,8 +1830,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, freqs.flags = 0; pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n", - __func__, policy->cpu, freqs.old, - freqs.new); + __func__, policy->cpu, freqs.old, freqs.new); cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); @@ -1822,7 +1839,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, retval = cpufreq_driver->target_index(policy, index); if (retval) pr_err("%s: Failed to change cpu frequency: %d\n", - __func__, retval); + __func__, retval); if (notify) cpufreq_notify_post_transition(policy, &freqs, retval); @@ -1868,17 +1885,18 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, struct cpufreq_governor *gov = NULL; #endif + /* Don't start any governor operations if we are entering suspend */ + if (cpufreq_suspended) + return 0; + if (policy->governor->max_transition_latency && policy->cpuinfo.transition_latency > policy->governor->max_transition_latency) { if (!gov) return -EINVAL; else { - printk(KERN_WARNING "%s governor failed, too long" - " transition latency of HW, fallback" - " to %s governor\n", - policy->governor->name, - gov->name); + pr_warn("%s governor failed, too long transition latency of HW, fallback to %s governor\n", + policy->governor->name, gov->name); policy->governor = gov; } } @@ -1888,7 +1906,7 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, return -EINVAL; pr_debug("__cpufreq_governor for CPU %u, event %u\n", - policy->cpu, event); + policy->cpu, event); mutex_lock(&cpufreq_governor_lock); if ((policy->governor_enabled && event == CPUFREQ_GOV_START) @@ -1955,9 +1973,7 @@ EXPORT_SYMBOL_GPL(cpufreq_register_governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor) { -#ifdef CONFIG_HOTPLUG_CPU int cpu; -#endif if (!governor) return; @@ -1965,14 +1981,12 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) if (cpufreq_disabled()) return; -#ifdef CONFIG_HOTPLUG_CPU for_each_present_cpu(cpu) { if (cpu_online(cpu)) continue; if (!strcmp(per_cpu(cpufreq_cpu_governor, cpu), governor->name)) strcpy(per_cpu(cpufreq_cpu_governor, cpu), "\0"); } -#endif mutex_lock(&cpufreq_governor_mutex); list_del(&governor->governor_list); @@ -2017,22 +2031,21 @@ EXPORT_SYMBOL(cpufreq_get_policy); static int cpufreq_set_policy(struct cpufreq_policy *policy, struct cpufreq_policy *new_policy) { - int ret = 0, failed = 1; + struct cpufreq_governor *old_gov; + int ret; - pr_debug("setting new policy for CPU %u: %u - %u kHz\n", new_policy->cpu, - new_policy->min, new_policy->max); + pr_debug("setting new policy for CPU %u: %u - %u kHz\n", + new_policy->cpu, new_policy->min, new_policy->max); memcpy(&new_policy->cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo)); - if (new_policy->min > policy->max || new_policy->max < policy->min) { - ret = -EINVAL; - goto error_out; - } + if (new_policy->min > policy->max || new_policy->max < policy->min) + return -EINVAL; /* verify the cpu speed can be set within this limit */ ret = cpufreq_driver->verify(new_policy); if (ret) - goto error_out; + return ret; /* adjust if necessary - all reasons */ blocking_notifier_call_chain(&cpufreq_policy_notifier_list, @@ -2048,7 +2061,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, */ ret = cpufreq_driver->verify(new_policy); if (ret) - goto error_out; + return ret; /* notification of the new policy */ blocking_notifier_call_chain(&cpufreq_policy_notifier_list, @@ -2058,63 +2071,53 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, policy->max = new_policy->max; pr_debug("new min and max freqs are %u - %u kHz\n", - policy->min, policy->max); + policy->min, policy->max); if (cpufreq_driver->setpolicy) { policy->policy = new_policy->policy; pr_debug("setting range\n"); - ret = cpufreq_driver->setpolicy(new_policy); - } else { - if (new_policy->governor != policy->governor) { - /* save old, working values */ - struct cpufreq_governor *old_gov = policy->governor; - - pr_debug("governor switch\n"); - - /* end old governor */ - if (policy->governor) { - __cpufreq_governor(policy, CPUFREQ_GOV_STOP); - up_write(&policy->rwsem); - __cpufreq_governor(policy, - CPUFREQ_GOV_POLICY_EXIT); - down_write(&policy->rwsem); - } + return cpufreq_driver->setpolicy(new_policy); + } - /* start new governor */ - policy->governor = new_policy->governor; - if (!__cpufreq_governor(policy, CPUFREQ_GOV_POLICY_INIT)) { - if (!__cpufreq_governor(policy, CPUFREQ_GOV_START)) { - failed = 0; - } else { - up_write(&policy->rwsem); - __cpufreq_governor(policy, - CPUFREQ_GOV_POLICY_EXIT); - down_write(&policy->rwsem); - } - } + if (new_policy->governor == policy->governor) + goto out; - if (failed) { - /* new governor failed, so re-start old one */ - pr_debug("starting governor %s failed\n", - policy->governor->name); - if (old_gov) { - policy->governor = old_gov; - __cpufreq_governor(policy, - CPUFREQ_GOV_POLICY_INIT); - __cpufreq_governor(policy, - CPUFREQ_GOV_START); - } - ret = -EINVAL; - goto error_out; - } - /* might be a policy change, too, so fall through */ - } - pr_debug("governor: change or update limits\n"); - ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); + pr_debug("governor switch\n"); + + /* save old, working values */ + old_gov = policy->governor; + /* end old governor */ + if (old_gov) { + __cpufreq_governor(policy, CPUFREQ_GOV_STOP); + up_write(&policy->rwsem); + __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); + down_write(&policy->rwsem); } -error_out: - return ret; + /* start new governor */ + policy->governor = new_policy->governor; + if (!__cpufreq_governor(policy, CPUFREQ_GOV_POLICY_INIT)) { + if (!__cpufreq_governor(policy, CPUFREQ_GOV_START)) + goto out; + + up_write(&policy->rwsem); + __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); + down_write(&policy->rwsem); + } + + /* new governor failed, so re-start old one */ + pr_debug("starting governor %s failed\n", policy->governor->name); + if (old_gov) { + policy->governor = old_gov; + __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_INIT); + __cpufreq_governor(policy, CPUFREQ_GOV_START); + } + + return -EINVAL; + + out: + pr_debug("governor: change or update limits\n"); + return __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); } /** @@ -2148,10 +2151,15 @@ int cpufreq_update_policy(unsigned int cpu) * BIOS might change freq behind our back * -> ask driver for current freq and notify governors about a change */ - if (cpufreq_driver->get) { + if (cpufreq_driver->get && !cpufreq_driver->setpolicy) { new_policy.cur = cpufreq_driver->get(cpu); + if (WARN_ON(!new_policy.cur)) { + ret = -EIO; + goto no_policy; + } + if (!policy->cur) { - pr_debug("Driver did not initialize current freq"); + pr_debug("Driver did not initialize current freq\n"); policy->cur = new_policy.cur; } else { if (policy->cur != new_policy.cur && has_target()) @@ -2175,30 +2183,24 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb, { unsigned int cpu = (unsigned long)hcpu; struct device *dev; - bool frozen = false; dev = get_cpu_device(cpu); if (dev) { - - if (action & CPU_TASKS_FROZEN) - frozen = true; - switch (action & ~CPU_TASKS_FROZEN) { case CPU_ONLINE: - __cpufreq_add_dev(dev, NULL, frozen); - cpufreq_update_policy(cpu); + __cpufreq_add_dev(dev, NULL); break; case CPU_DOWN_PREPARE: - __cpufreq_remove_dev_prepare(dev, NULL, frozen); + __cpufreq_remove_dev_prepare(dev, NULL); break; case CPU_POST_DEAD: - __cpufreq_remove_dev_finish(dev, NULL, frozen); + __cpufreq_remove_dev_finish(dev, NULL); break; case CPU_DOWN_FAILED: - __cpufreq_add_dev(dev, NULL, frozen); + __cpufreq_add_dev(dev, NULL); break; } } @@ -2254,8 +2256,8 @@ int cpufreq_boost_trigger_state(int state) cpufreq_driver->boost_enabled = !state; write_unlock_irqrestore(&cpufreq_driver_lock, flags); - pr_err("%s: Cannot %s BOOST\n", __func__, - state ? "enable" : "disable"); + pr_err("%s: Cannot %s BOOST\n", + __func__, state ? "enable" : "disable"); } return ret; @@ -2300,7 +2302,9 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) if (!driver_data || !driver_data->verify || !driver_data->init || !(driver_data->setpolicy || driver_data->target_index || - driver_data->target)) + driver_data->target) || + (driver_data->setpolicy && (driver_data->target_index || + driver_data->target))) return -EINVAL; pr_debug("trying to register driver %s\n", driver_data->name); @@ -2327,7 +2331,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) ret = cpufreq_sysfs_create_file(&boost.attr); if (ret) { pr_err("%s: cannot register global BOOST sysfs file\n", - __func__); + __func__); goto err_null_driver; } } @@ -2350,7 +2354,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) /* if all ->init() calls failed, unregister */ if (ret) { pr_debug("no CPU initialized for driver %s\n", - driver_data->name); + driver_data->name); goto err_if_unreg; } } @@ -2414,7 +2418,6 @@ 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; } diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 5793e1447fb1..ecaaebf969fc 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -13,7 +13,7 @@ #include <linux/cpufreq.h> #include <linux/module.h> #include <linux/slab.h> -#include <asm/cputime.h> +#include <linux/cputime.h> static spinlock_t cpufreq_stats_lock; @@ -180,27 +180,25 @@ static void cpufreq_stats_free_table(unsigned int cpu) cpufreq_cpu_put(policy); } -static int __cpufreq_stats_create_table(struct cpufreq_policy *policy, - struct cpufreq_frequency_table *table) +static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) { unsigned int i, j, count = 0, ret = 0; struct cpufreq_stats *stat; - struct cpufreq_policy *current_policy; unsigned int alloc_size; unsigned int cpu = policy->cpu; + struct cpufreq_frequency_table *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; - current_policy = cpufreq_cpu_get(cpu); - if (current_policy == NULL) { - ret = -EINVAL; - goto error_get_fail; - } - - ret = sysfs_create_group(¤t_policy->kobj, &stats_attr_group); + ret = sysfs_create_group(&policy->kobj, &stats_attr_group); if (ret) goto error_out; @@ -223,7 +221,7 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy, stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL); if (!stat->time_in_state) { ret = -ENOMEM; - goto error_out; + goto error_alloc; } stat->freq_table = (unsigned int *)(stat->time_in_state + count); @@ -243,11 +241,10 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy, stat->last_time = get_jiffies_64(); stat->last_index = freq_table_get_index(stat, policy->cur); spin_unlock(&cpufreq_stats_lock); - cpufreq_cpu_put(current_policy); return 0; +error_alloc: + sysfs_remove_group(&policy->kobj, &stats_attr_group); error_out: - cpufreq_cpu_put(current_policy); -error_get_fail: kfree(stat); per_cpu(cpufreq_stats_table, cpu) = NULL; return ret; @@ -256,7 +253,6 @@ error_get_fail: static void cpufreq_stats_create_table(unsigned int cpu) { struct cpufreq_policy *policy; - struct cpufreq_frequency_table *table; /* * "likely(!policy)" because normally cpufreq_stats will be registered @@ -266,9 +262,7 @@ static void cpufreq_stats_create_table(unsigned int cpu) if (likely(!policy)) return; - table = cpufreq_frequency_get_table(policy->cpu); - if (likely(table)) - __cpufreq_stats_create_table(policy, table); + __cpufreq_stats_create_table(policy); cpufreq_cpu_put(policy); } @@ -291,20 +285,14 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb, { int ret = 0; struct cpufreq_policy *policy = data; - struct cpufreq_frequency_table *table; - unsigned int cpu = policy->cpu; if (val == CPUFREQ_UPDATE_POLICY_CPU) { cpufreq_stats_update_policy_cpu(policy); return 0; } - table = cpufreq_frequency_get_table(cpu); - if (!table) - return 0; - if (val == CPUFREQ_CREATE_POLICY) - ret = __cpufreq_stats_create_table(policy, table); + ret = __cpufreq_stats_create_table(policy); else if (val == CPUFREQ_REMOVE_POLICY) __cpufreq_stats_free_table(policy); diff --git a/drivers/cpufreq/cris-artpec3-cpufreq.c b/drivers/cpufreq/cris-artpec3-cpufreq.c index 86559040c54c..d4573032cbbc 100644 --- a/drivers/cpufreq/cris-artpec3-cpufreq.c +++ b/drivers/cpufreq/cris-artpec3-cpufreq.c @@ -57,7 +57,6 @@ static struct cpufreq_driver cris_freq_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = cris_freq_target, .init = cris_freq_cpu_init, - .exit = cpufreq_generic_exit, .name = "cris_freq", .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/cris-etraxfs-cpufreq.c b/drivers/cpufreq/cris-etraxfs-cpufreq.c index 26d940d40b1d..13c3361437f7 100644 --- a/drivers/cpufreq/cris-etraxfs-cpufreq.c +++ b/drivers/cpufreq/cris-etraxfs-cpufreq.c @@ -57,7 +57,6 @@ static struct cpufreq_driver cris_freq_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = cris_freq_target, .init = cris_freq_cpu_init, - .exit = cpufreq_generic_exit, .name = "cris_freq", .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/davinci-cpufreq.c b/drivers/cpufreq/davinci-cpufreq.c index 2cf33848d86e..28a16dc6e02e 100644 --- a/drivers/cpufreq/davinci-cpufreq.c +++ b/drivers/cpufreq/davinci-cpufreq.c @@ -125,7 +125,6 @@ static struct cpufreq_driver davinci_driver = { .target_index = davinci_target, .get = cpufreq_generic_get, .init = davinci_cpu_init, - .exit = cpufreq_generic_exit, .name = "davinci", .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c index 9012b8bb6b64..a0d2a423cea9 100644 --- a/drivers/cpufreq/e_powersaver.c +++ b/drivers/cpufreq/e_powersaver.c @@ -382,7 +382,6 @@ static int eps_cpu_exit(struct cpufreq_policy *policy) unsigned int cpu = policy->cpu; /* Bye */ - cpufreq_frequency_table_put_attr(policy->cpu); kfree(eps_cpu[cpu]); eps_cpu[cpu] = NULL; return 0; diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c index de08acff5101..c987e94708f5 100644 --- a/drivers/cpufreq/elanfreq.c +++ b/drivers/cpufreq/elanfreq.c @@ -198,7 +198,6 @@ static struct cpufreq_driver elanfreq_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = elanfreq_target, .init = elanfreq_cpu_init, - .exit = cpufreq_generic_exit, .name = "elanfreq", .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index fcd2914d081a..f99cfe24e7bc 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -16,7 +16,6 @@ #include <linux/slab.h> #include <linux/regulator/consumer.h> #include <linux/cpufreq.h> -#include <linux/suspend.h> #include <linux/platform_device.h> #include <plat/cpu.h> @@ -24,12 +23,8 @@ #include "exynos-cpufreq.h" static struct exynos_dvfs_info *exynos_info; - static struct regulator *arm_regulator; - static unsigned int locking_frequency; -static bool frequency_locked; -static DEFINE_MUTEX(cpufreq_lock); static int exynos_cpufreq_get_index(unsigned int freq) { @@ -134,83 +129,13 @@ out: static int exynos_target(struct cpufreq_policy *policy, unsigned int index) { - struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; - int ret = 0; - - mutex_lock(&cpufreq_lock); - - if (frequency_locked) - goto out; - - ret = exynos_cpufreq_scale(freq_table[index].frequency); - -out: - mutex_unlock(&cpufreq_lock); - - return ret; -} - -#ifdef CONFIG_PM -static int exynos_cpufreq_suspend(struct cpufreq_policy *policy) -{ - return 0; -} - -static int exynos_cpufreq_resume(struct cpufreq_policy *policy) -{ - return 0; -} -#endif - -/** - * exynos_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume - * context - * @notifier - * @pm_event - * @v - * - * While frequency_locked == true, target() ignores every frequency but - * locking_frequency. The locking_frequency value is the initial frequency, - * which is set by the bootloader. In order to eliminate possible - * inconsistency in clock values, we save and restore frequencies during - * suspend and resume and block CPUFREQ activities. Note that the standard - * suspend/resume cannot be used as they are too deep (syscore_ops) for - * regulator actions. - */ -static int exynos_cpufreq_pm_notifier(struct notifier_block *notifier, - unsigned long pm_event, void *v) -{ - int ret; - - switch (pm_event) { - case PM_SUSPEND_PREPARE: - mutex_lock(&cpufreq_lock); - frequency_locked = true; - mutex_unlock(&cpufreq_lock); - - ret = exynos_cpufreq_scale(locking_frequency); - if (ret < 0) - return NOTIFY_BAD; - - break; - - case PM_POST_SUSPEND: - mutex_lock(&cpufreq_lock); - frequency_locked = false; - mutex_unlock(&cpufreq_lock); - break; - } - - return NOTIFY_OK; + return exynos_cpufreq_scale(exynos_info->freq_table[index].frequency); } -static struct notifier_block exynos_cpufreq_nb = { - .notifier_call = exynos_cpufreq_pm_notifier, -}; - static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) { policy->clk = exynos_info->cpu_clk; + policy->suspend_freq = locking_frequency; return cpufreq_generic_init(policy, exynos_info->freq_table, 100000); } @@ -220,15 +145,13 @@ static struct cpufreq_driver exynos_driver = { .target_index = exynos_target, .get = cpufreq_generic_get, .init = exynos_cpufreq_cpu_init, - .exit = cpufreq_generic_exit, .name = "exynos_cpufreq", .attr = cpufreq_generic_attr, #ifdef CONFIG_ARM_EXYNOS_CPU_FREQ_BOOST_SW .boost_supported = true, #endif #ifdef CONFIG_PM - .suspend = exynos_cpufreq_suspend, - .resume = exynos_cpufreq_resume, + .suspend = cpufreq_generic_suspend, #endif }; @@ -263,19 +186,13 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) goto err_vdd_arm; } + /* Done here as we want to capture boot frequency */ locking_frequency = clk_get_rate(exynos_info->cpu_clk) / 1000; - register_pm_notifier(&exynos_cpufreq_nb); - - if (cpufreq_register_driver(&exynos_driver)) { - pr_err("%s: failed to register cpufreq driver\n", __func__); - goto err_cpufreq; - } - - return 0; -err_cpufreq: - unregister_pm_notifier(&exynos_cpufreq_nb); + if (!cpufreq_register_driver(&exynos_driver)) + return 0; + pr_err("%s: failed to register cpufreq driver\n", __func__); regulator_put(arm_regulator); err_vdd_arm: kfree(exynos_info); diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c index 49b756015316..7f776aa91e2f 100644 --- a/drivers/cpufreq/exynos5440-cpufreq.c +++ b/drivers/cpufreq/exynos5440-cpufreq.c @@ -312,7 +312,6 @@ static struct cpufreq_driver exynos_driver = { .target_index = exynos_target, .get = cpufreq_generic_get, .init = exynos_cpufreq_cpu_init, - .exit = cpufreq_generic_exit, .name = CPUFREQ_NAME, .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 8e54f97899ba..65a477075b3f 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -91,8 +91,8 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify); /* - * Generic routine to verify policy & frequency table, requires driver to call - * cpufreq_frequency_table_get_attr() prior to it. + * Generic routine to verify policy & frequency table, requires driver to set + * policy->freq_table prior to it. */ int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy) { @@ -203,8 +203,6 @@ int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index); -static DEFINE_PER_CPU(struct cpufreq_frequency_table *, cpufreq_show_table); - /** * show_available_freqs - show available frequencies for the specified CPU */ @@ -212,15 +210,12 @@ static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf, bool show_boost) { unsigned int i = 0; - unsigned int cpu = policy->cpu; ssize_t count = 0; - struct cpufreq_frequency_table *table; + struct cpufreq_frequency_table *table = policy->freq_table; - if (!per_cpu(cpufreq_show_table, cpu)) + if (!table) return -ENODEV; - table = per_cpu(cpufreq_show_table, cpu); - for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { if (table[i].frequency == CPUFREQ_ENTRY_INVALID) continue; @@ -283,49 +278,24 @@ struct freq_attr *cpufreq_generic_attr[] = { }; EXPORT_SYMBOL_GPL(cpufreq_generic_attr); -/* - * if you use these, you must assure that the frequency table is valid - * all the time between get_attr and put_attr! - */ -void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, - unsigned int cpu) -{ - pr_debug("setting show_table for cpu %u to %p\n", cpu, table); - per_cpu(cpufreq_show_table, cpu) = table; -} -EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr); - -void cpufreq_frequency_table_put_attr(unsigned int cpu) -{ - pr_debug("clearing show_table for cpu %u\n", cpu); - per_cpu(cpufreq_show_table, cpu) = NULL; -} -EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr); - int cpufreq_table_validate_and_show(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table) { int ret = cpufreq_frequency_table_cpuinfo(policy, table); if (!ret) - cpufreq_frequency_table_get_attr(table, policy->cpu); + policy->freq_table = table; return ret; } EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show); -void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy) -{ - pr_debug("Updating show_table for new_cpu %u from last_cpu %u\n", - policy->cpu, policy->last_cpu); - per_cpu(cpufreq_show_table, policy->cpu) = per_cpu(cpufreq_show_table, - policy->last_cpu); - per_cpu(cpufreq_show_table, policy->last_cpu) = NULL; -} +struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu); struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu) { - return per_cpu(cpufreq_show_table, cpu); + struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); + return policy ? policy->freq_table : NULL; } EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table); diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c index 53c6ac637e10..a22b5d182e0e 100644 --- a/drivers/cpufreq/ia64-acpi-cpufreq.c +++ b/drivers/cpufreq/ia64-acpi-cpufreq.c @@ -332,7 +332,6 @@ acpi_cpufreq_cpu_exit ( pr_debug("acpi_cpufreq_cpu_exit\n"); if (data) { - cpufreq_frequency_table_put_attr(policy->cpu); acpi_io_data[policy->cpu] = NULL; acpi_processor_unregister_performance(&data->acpi_data, policy->cpu); diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index ce69059be1fc..e27fca86fe4f 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -144,7 +144,6 @@ static struct cpufreq_driver imx6q_cpufreq_driver = { .target_index = imx6q_set_target, .get = cpufreq_generic_get, .init = imx6q_cpufreq_init, - .exit = cpufreq_generic_exit, .name = "imx6q-cpufreq", .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 2cd36b9297f3..bcb9a6d0ae11 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -99,8 +99,7 @@ struct cpudata { u64 prev_aperf; u64 prev_mperf; unsigned long long prev_tsc; - int sample_ptr; - struct sample samples[SAMPLE_COUNT]; + struct sample sample; }; static struct cpudata **all_cpu_data; @@ -154,7 +153,7 @@ static inline void pid_reset(struct _pid *pid, int setpoint, int busy, pid->setpoint = setpoint; pid->deadband = deadband; pid->integral = int_tofp(integral); - pid->last_err = setpoint - busy; + pid->last_err = int_tofp(setpoint) - int_tofp(busy); } static inline void pid_p_gain_set(struct _pid *pid, int percent) @@ -447,7 +446,7 @@ static void core_set_pstate(struct cpudata *cpudata, int pstate) if (limits.no_turbo) val |= (u64)1 << 32; - wrmsrl(MSR_IA32_PERF_CTL, val); + wrmsrl_on_cpu(cpudata->cpu, MSR_IA32_PERF_CTL, val); } static struct cpu_defaults core_params = { @@ -586,15 +585,14 @@ static inline void intel_pstate_sample(struct cpudata *cpu) mperf = mperf >> FRAC_BITS; tsc = tsc >> FRAC_BITS; - cpu->sample_ptr = (cpu->sample_ptr + 1) % SAMPLE_COUNT; - cpu->samples[cpu->sample_ptr].aperf = aperf; - cpu->samples[cpu->sample_ptr].mperf = mperf; - cpu->samples[cpu->sample_ptr].tsc = tsc; - cpu->samples[cpu->sample_ptr].aperf -= cpu->prev_aperf; - cpu->samples[cpu->sample_ptr].mperf -= cpu->prev_mperf; - cpu->samples[cpu->sample_ptr].tsc -= cpu->prev_tsc; + cpu->sample.aperf = aperf; + cpu->sample.mperf = mperf; + cpu->sample.tsc = tsc; + cpu->sample.aperf -= cpu->prev_aperf; + cpu->sample.mperf -= cpu->prev_mperf; + cpu->sample.tsc -= cpu->prev_tsc; - intel_pstate_calc_busy(cpu, &cpu->samples[cpu->sample_ptr]); + intel_pstate_calc_busy(cpu, &cpu->sample); cpu->prev_aperf = aperf; cpu->prev_mperf = mperf; @@ -614,7 +612,7 @@ static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu) { int32_t core_busy, max_pstate, current_pstate; - core_busy = cpu->samples[cpu->sample_ptr].core_pct_busy; + core_busy = cpu->sample.core_pct_busy; max_pstate = int_tofp(cpu->pstate.max_pstate); current_pstate = int_tofp(cpu->pstate.current_pstate); core_busy = mul_fp(core_busy, div_fp(max_pstate, current_pstate)); @@ -648,7 +646,7 @@ static void intel_pstate_timer_func(unsigned long __data) intel_pstate_sample(cpu); - sample = &cpu->samples[cpu->sample_ptr]; + sample = &cpu->sample; intel_pstate_adjust_busy_pstate(cpu); @@ -729,7 +727,7 @@ static unsigned int intel_pstate_get(unsigned int cpu_num) cpu = all_cpu_data[cpu_num]; if (!cpu) return 0; - sample = &cpu->samples[cpu->sample_ptr]; + sample = &cpu->sample; return sample->freq; } @@ -773,14 +771,17 @@ static int intel_pstate_verify_policy(struct cpufreq_policy *policy) return 0; } -static int intel_pstate_cpu_exit(struct cpufreq_policy *policy) +static void intel_pstate_stop_cpu(struct cpufreq_policy *policy) { - int cpu = policy->cpu; + int cpu_num = policy->cpu; + struct cpudata *cpu = all_cpu_data[cpu_num]; - del_timer(&all_cpu_data[cpu]->timer); - kfree(all_cpu_data[cpu]); - all_cpu_data[cpu] = NULL; - return 0; + pr_info("intel_pstate CPU %d exiting\n", cpu_num); + + del_timer(&all_cpu_data[cpu_num]->timer); + intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); + kfree(all_cpu_data[cpu_num]); + all_cpu_data[cpu_num] = NULL; } static int intel_pstate_cpu_init(struct cpufreq_policy *policy) @@ -818,7 +819,7 @@ static struct cpufreq_driver intel_pstate_driver = { .setpolicy = intel_pstate_set_policy, .get = intel_pstate_get, .init = intel_pstate_cpu_init, - .exit = intel_pstate_cpu_exit, + .stop_cpu = intel_pstate_stop_cpu, .name = "intel_pstate", }; diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c index eb7abe345b50..3d114bc5a97a 100644 --- a/drivers/cpufreq/kirkwood-cpufreq.c +++ b/drivers/cpufreq/kirkwood-cpufreq.c @@ -102,7 +102,6 @@ static struct cpufreq_driver kirkwood_cpufreq_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = kirkwood_cpufreq_target, .init = kirkwood_cpufreq_cpu_init, - .exit = cpufreq_generic_exit, .name = "kirkwood-cpufreq", .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index 45bafddfd8ea..7b94da3d2d10 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -913,7 +913,6 @@ static struct cpufreq_driver longhaul_driver = { .target_index = longhaul_target, .get = longhaul_get, .init = longhaul_cpu_init, - .exit = cpufreq_generic_exit, .name = "longhaul", .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c index b6581abc9207..a3588d61d933 100644 --- a/drivers/cpufreq/loongson2_cpufreq.c +++ b/drivers/cpufreq/loongson2_cpufreq.c @@ -104,7 +104,6 @@ static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy) static int loongson2_cpufreq_exit(struct cpufreq_policy *policy) { - cpufreq_frequency_table_put_attr(policy->cpu); clk_put(policy->clk); return 0; } diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 590f5b66d181..5f69c9aa703c 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -143,7 +143,6 @@ fail: static int omap_cpu_exit(struct cpufreq_policy *policy) { - cpufreq_frequency_table_put_attr(policy->cpu); freq_table_free(); clk_put(policy->clk); return 0; diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c index 3d1cba9fd5f9..74f593e70e19 100644 --- a/drivers/cpufreq/p4-clockmod.c +++ b/drivers/cpufreq/p4-clockmod.c @@ -237,7 +237,6 @@ static struct cpufreq_driver p4clockmod_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = cpufreq_p4_target, .init = cpufreq_p4_cpu_init, - .exit = cpufreq_generic_exit, .get = cpufreq_p4_get, .name = "p4-clockmod", .attr = cpufreq_generic_attr, diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c index 0426008380d8..6a2b7d3e85a7 100644 --- a/drivers/cpufreq/pasemi-cpufreq.c +++ b/drivers/cpufreq/pasemi-cpufreq.c @@ -234,7 +234,6 @@ static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy) if (sdcpwr_mapbase) iounmap(sdcpwr_mapbase); - cpufreq_frequency_table_put_attr(policy->cpu); return 0; } diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index b9a444e358b5..ce27e6c26c94 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -231,7 +231,6 @@ static int powernow_k6_cpu_exit(struct cpufreq_policy *policy) if (i == max_multiplier) powernow_k6_target(policy, i); } - cpufreq_frequency_table_put_attr(policy->cpu); return 0; } diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c index 946708a1d745..0e68e0275621 100644 --- a/drivers/cpufreq/powernow-k7.c +++ b/drivers/cpufreq/powernow-k7.c @@ -664,8 +664,6 @@ static int powernow_cpu_init(struct cpufreq_policy *policy) static int powernow_cpu_exit(struct cpufreq_policy *policy) { - cpufreq_frequency_table_put_attr(policy->cpu); - #ifdef CONFIG_X86_POWERNOW_K7_ACPI if (acpi_processor_perf) { acpi_processor_unregister_performance(acpi_processor_perf, 0); diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index 6684e0342792..27eb2be44de5 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -1164,8 +1164,6 @@ static int powernowk8_cpu_exit(struct cpufreq_policy *pol) powernow_k8_cpu_exit_acpi(data); - cpufreq_frequency_table_put_attr(pol->cpu); - kfree(data->powernow_table); kfree(data); for_each_cpu(cpu, pol->cpus) diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c index 051000f44ca2..3bd9123e7026 100644 --- a/drivers/cpufreq/ppc-corenet-cpufreq.c +++ b/drivers/cpufreq/ppc-corenet-cpufreq.c @@ -21,6 +21,7 @@ #include <linux/of.h> #include <linux/slab.h> #include <linux/smp.h> +#include <sysdev/fsl_soc.h> /** * struct cpu_data - per CPU data struct @@ -205,7 +206,8 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) for_each_cpu(i, per_cpu(cpu_mask, cpu)) per_cpu(cpu_data, i) = data; - policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + policy->cpuinfo.transition_latency = + (12 * NSEC_PER_SEC) / fsl_get_sys_freq(); of_node_put(np); return 0; @@ -228,7 +230,6 @@ static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy) struct cpu_data *data = per_cpu(cpu_data, policy->cpu); unsigned int cpu; - cpufreq_frequency_table_put_attr(policy->cpu); of_node_put(data->parent); kfree(data->table); kfree(data); diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c index e42ca9c31cea..af7b1cabd1e7 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq.c +++ b/drivers/cpufreq/ppc_cbe_cpufreq.c @@ -141,7 +141,6 @@ static struct cpufreq_driver cbe_cpufreq_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = cbe_cpufreq_target, .init = cbe_cpufreq_cpu_init, - .exit = cpufreq_generic_exit, .name = "cbe-cpufreq", .flags = CPUFREQ_CONST_LOOPS, }; diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c index a9195a86b069..e24269ab4e9b 100644 --- a/drivers/cpufreq/pxa2xx-cpufreq.c +++ b/drivers/cpufreq/pxa2xx-cpufreq.c @@ -427,7 +427,6 @@ static struct cpufreq_driver pxa_cpufreq_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = pxa_set_target, .init = pxa_cpufreq_init, - .exit = cpufreq_generic_exit, .get = pxa_cpufreq_get, .name = "PXA2xx", }; diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c index 3785687e9d70..a01275900389 100644 --- a/drivers/cpufreq/pxa3xx-cpufreq.c +++ b/drivers/cpufreq/pxa3xx-cpufreq.c @@ -205,7 +205,6 @@ static struct cpufreq_driver pxa3xx_cpufreq_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = pxa3xx_cpufreq_set, .init = pxa3xx_cpufreq_init, - .exit = cpufreq_generic_exit, .get = pxa3xx_cpufreq_get, .name = "pxa3xx-cpufreq", }; diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index 55a8e9fa9435..72421534fff5 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -18,7 +18,6 @@ #include <linux/cpufreq.h> #include <linux/reboot.h> #include <linux/regulator/consumer.h> -#include <linux/suspend.h> #include <mach/map.h> #include <mach/regs-clock.h> @@ -435,18 +434,6 @@ exit: return ret; } -#ifdef CONFIG_PM -static int s5pv210_cpufreq_suspend(struct cpufreq_policy *policy) -{ - return 0; -} - -static int s5pv210_cpufreq_resume(struct cpufreq_policy *policy) -{ - return 0; -} -#endif - static int check_mem_type(void __iomem *dmc_reg) { unsigned long val; @@ -502,6 +489,7 @@ static int __init s5pv210_cpu_init(struct cpufreq_policy *policy) s5pv210_dram_conf[1].refresh = (__raw_readl(S5P_VA_DMC1 + 0x30) * 1000); s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk); + policy->suspend_freq = SLEEP_FREQ; return cpufreq_generic_init(policy, s5pv210_freq_table, 40000); out_dmc1: @@ -511,32 +499,6 @@ out_dmc0: return ret; } -static int s5pv210_cpufreq_notifier_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - int ret; - - switch (event) { - case PM_SUSPEND_PREPARE: - ret = cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ, 0); - if (ret < 0) - return NOTIFY_BAD; - - /* Disable updation of cpu frequency */ - no_cpufreq_access = true; - return NOTIFY_OK; - case PM_POST_RESTORE: - case PM_POST_SUSPEND: - /* Enable updation of cpu frequency */ - no_cpufreq_access = false; - cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ, 0); - - return NOTIFY_OK; - } - - return NOTIFY_DONE; -} - static int s5pv210_cpufreq_reboot_notifier_event(struct notifier_block *this, unsigned long event, void *ptr) { @@ -558,15 +520,11 @@ static struct cpufreq_driver s5pv210_driver = { .init = s5pv210_cpu_init, .name = "s5pv210", #ifdef CONFIG_PM - .suspend = s5pv210_cpufreq_suspend, - .resume = s5pv210_cpufreq_resume, + .suspend = cpufreq_generic_suspend, + .resume = cpufreq_generic_suspend, /* We need to set SLEEP FREQ again */ #endif }; -static struct notifier_block s5pv210_cpufreq_notifier = { - .notifier_call = s5pv210_cpufreq_notifier_event, -}; - static struct notifier_block s5pv210_cpufreq_reboot_notifier = { .notifier_call = s5pv210_cpufreq_reboot_notifier_event, }; @@ -586,7 +544,6 @@ static int __init s5pv210_cpufreq_init(void) return PTR_ERR(int_regulator); } - register_pm_notifier(&s5pv210_cpufreq_notifier); register_reboot_notifier(&s5pv210_cpufreq_reboot_notifier); return cpufreq_register_driver(&s5pv210_driver); diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c index 6adb354e359c..69371bf0886d 100644 --- a/drivers/cpufreq/sc520_freq.c +++ b/drivers/cpufreq/sc520_freq.c @@ -93,7 +93,6 @@ static struct cpufreq_driver sc520_freq_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = sc520_freq_target, .init = sc520_freq_cpu_init, - .exit = cpufreq_generic_exit, .name = "sc520_freq", .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c index 387af12503a6..696170ebd3a3 100644 --- a/drivers/cpufreq/sh-cpufreq.c +++ b/drivers/cpufreq/sh-cpufreq.c @@ -143,7 +143,6 @@ static int sh_cpufreq_cpu_exit(struct cpufreq_policy *policy) unsigned int cpu = policy->cpu; struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu); - cpufreq_frequency_table_put_attr(cpu); clk_put(cpuclk); return 0; diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c index 62aa23e219d4..b73feeb666f9 100644 --- a/drivers/cpufreq/sparc-us2e-cpufreq.c +++ b/drivers/cpufreq/sparc-us2e-cpufreq.c @@ -301,10 +301,8 @@ static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy) static int us2e_freq_cpu_exit(struct cpufreq_policy *policy) { - if (cpufreq_us2e_driver) { - cpufreq_frequency_table_put_attr(policy->cpu); + if (cpufreq_us2e_driver) us2e_freq_target(policy, 0); - } return 0; } diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c index 724ffbd7105d..9bb42ba50efa 100644 --- a/drivers/cpufreq/sparc-us3-cpufreq.c +++ b/drivers/cpufreq/sparc-us3-cpufreq.c @@ -156,10 +156,8 @@ static int __init us3_freq_cpu_init(struct cpufreq_policy *policy) static int us3_freq_cpu_exit(struct cpufreq_policy *policy) { - if (cpufreq_us3_driver) { - cpufreq_frequency_table_put_attr(policy->cpu); + if (cpufreq_us3_driver) us3_freq_target(policy, 0); - } return 0; } diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index 5c86e3fa5593..4cfdcff8a310 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -19,6 +19,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/platform_device.h> #include <linux/slab.h> #include <linux/types.h> @@ -163,11 +164,10 @@ static struct cpufreq_driver spear_cpufreq_driver = { .target_index = spear_cpufreq_target, .get = cpufreq_generic_get, .init = spear_cpufreq_init, - .exit = cpufreq_generic_exit, .attr = cpufreq_generic_attr, }; -static int spear_cpufreq_driver_init(void) +static int spear_cpufreq_probe(struct platform_device *pdev) { struct device_node *np; const struct property *prop; @@ -235,7 +235,15 @@ out_put_node: of_node_put(np); return ret; } -late_initcall(spear_cpufreq_driver_init); + +static struct platform_driver spear_cpufreq_platdrv = { + .driver = { + .name = "spear-cpufreq", + .owner = THIS_MODULE, + }, + .probe = spear_cpufreq_probe, +}; +module_platform_driver(spear_cpufreq_platdrv); MODULE_AUTHOR("Deepak Sikri <deepak.sikri@st.com>"); MODULE_DESCRIPTION("SPEAr CPUFreq driver"); diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c index 4e1daca5ce3b..6723f0390f20 100644 --- a/drivers/cpufreq/speedstep-centrino.c +++ b/drivers/cpufreq/speedstep-centrino.c @@ -406,8 +406,6 @@ static int centrino_cpu_exit(struct cpufreq_policy *policy) if (!per_cpu(centrino_model, cpu)) return -ENODEV; - cpufreq_frequency_table_put_attr(cpu); - per_cpu(centrino_model, cpu) = NULL; return 0; diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c index 7639b2be2a90..394ac159312a 100644 --- a/drivers/cpufreq/speedstep-ich.c +++ b/drivers/cpufreq/speedstep-ich.c @@ -311,7 +311,6 @@ static struct cpufreq_driver speedstep_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = speedstep_target, .init = speedstep_cpu_init, - .exit = cpufreq_generic_exit, .get = speedstep_get, .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c index 998c17b42200..db5d274dc13a 100644 --- a/drivers/cpufreq/speedstep-smi.c +++ b/drivers/cpufreq/speedstep-smi.c @@ -280,7 +280,6 @@ static struct cpufreq_driver speedstep_driver = { .verify = cpufreq_generic_frequency_table_verify, .target_index = speedstep_target, .init = speedstep_cpu_init, - .exit = cpufreq_generic_exit, .get = speedstep_get, .resume = speedstep_resume, .attr = cpufreq_generic_attr, diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c index e652c1bd8d0f..63f00598a251 100644 --- a/drivers/cpufreq/tegra-cpufreq.c +++ b/drivers/cpufreq/tegra-cpufreq.c @@ -26,7 +26,6 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/io.h> -#include <linux/suspend.h> static struct cpufreq_frequency_table freq_table[] = { { .frequency = 216000 }, @@ -47,9 +46,6 @@ static struct clk *pll_x_clk; static struct clk *pll_p_clk; static struct clk *emc_clk; -static DEFINE_MUTEX(tegra_cpu_lock); -static bool is_suspended; - static int tegra_cpu_clk_set_rate(unsigned long rate) { int ret; @@ -112,42 +108,9 @@ static int tegra_update_cpu_speed(struct cpufreq_policy *policy, static int tegra_target(struct cpufreq_policy *policy, unsigned int index) { - int ret = -EBUSY; - - mutex_lock(&tegra_cpu_lock); - - if (!is_suspended) - ret = tegra_update_cpu_speed(policy, - freq_table[index].frequency); - - mutex_unlock(&tegra_cpu_lock); - return ret; + return tegra_update_cpu_speed(policy, freq_table[index].frequency); } -static int tegra_pm_notify(struct notifier_block *nb, unsigned long event, - void *dummy) -{ - mutex_lock(&tegra_cpu_lock); - if (event == PM_SUSPEND_PREPARE) { - struct cpufreq_policy *policy = cpufreq_cpu_get(0); - is_suspended = true; - pr_info("Tegra cpufreq suspend: setting frequency to %d kHz\n", - freq_table[0].frequency); - if (clk_get_rate(cpu_clk) / 1000 != freq_table[0].frequency) - tegra_update_cpu_speed(policy, freq_table[0].frequency); - cpufreq_cpu_put(policy); - } else if (event == PM_POST_SUSPEND) { - is_suspended = false; - } - mutex_unlock(&tegra_cpu_lock); - - return NOTIFY_OK; -} - -static struct notifier_block tegra_cpu_pm_notifier = { - .notifier_call = tegra_pm_notify, -}; - static int tegra_cpu_init(struct cpufreq_policy *policy) { int ret; @@ -166,16 +129,13 @@ static int tegra_cpu_init(struct cpufreq_policy *policy) return ret; } - if (policy->cpu == 0) - register_pm_notifier(&tegra_cpu_pm_notifier); - policy->clk = cpu_clk; + policy->suspend_freq = freq_table[0].frequency; return 0; } static int tegra_cpu_exit(struct cpufreq_policy *policy) { - cpufreq_frequency_table_put_attr(policy->cpu); clk_disable_unprepare(cpu_clk); clk_disable_unprepare(emc_clk); return 0; @@ -190,6 +150,9 @@ static struct cpufreq_driver tegra_cpufreq_driver = { .exit = tegra_cpu_exit, .name = "tegra", .attr = cpufreq_generic_attr, +#ifdef CONFIG_PM + .suspend = cpufreq_generic_suspend, +#endif }; static int __init tegra_cpufreq_init(void) diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index 78fd174c57e8..f48607cd2540 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -14,6 +14,7 @@ #include <asm/machdep.h> #include <asm/firmware.h> +#include <asm/runlatch.h> struct cpuidle_driver powernv_idle_driver = { .name = "powernv_idle", @@ -30,12 +31,14 @@ static int snooze_loop(struct cpuidle_device *dev, local_irq_enable(); set_thread_flag(TIF_POLLING_NRFLAG); + ppc64_runlatch_off(); while (!need_resched()) { HMT_low(); HMT_very_low(); } HMT_medium(); + ppc64_runlatch_on(); clear_thread_flag(TIF_POLLING_NRFLAG); smp_mb(); return index; @@ -45,7 +48,9 @@ static int nap_loop(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { + ppc64_runlatch_off(); power7_idle(); + ppc64_runlatch_on(); return index; } diff --git a/drivers/cpuidle/cpuidle-pseries.c b/drivers/cpuidle/cpuidle-pseries.c index 7ab564aa0b1c..6f7b01956885 100644 --- a/drivers/cpuidle/cpuidle-pseries.c +++ b/drivers/cpuidle/cpuidle-pseries.c @@ -17,6 +17,7 @@ #include <asm/reg.h> #include <asm/machdep.h> #include <asm/firmware.h> +#include <asm/runlatch.h> #include <asm/plpar_wrappers.h> struct cpuidle_driver pseries_idle_driver = { @@ -29,6 +30,7 @@ static struct cpuidle_state *cpuidle_state_table; static inline void idle_loop_prolog(unsigned long *in_purr) { + ppc64_runlatch_off(); *in_purr = mfspr(SPRN_PURR); /* * Indicate to the HV that we are idle. Now would be @@ -45,6 +47,10 @@ static inline void idle_loop_epilog(unsigned long in_purr) wait_cycles += mfspr(SPRN_PURR) - in_purr; get_lppaca()->wait_state_cycles = cpu_to_be64(wait_cycles); get_lppaca()->idle = 0; + + if (irqs_disabled()) + local_irq_enable(); + ppc64_runlatch_on(); } static int snooze_loop(struct cpuidle_device *dev, diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index a55e68f2cfc8..cb20fd915be8 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -85,7 +85,8 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, time_end = ktime_get(); - local_irq_enable(); + if (!cpuidle_state_is_coupled(dev, drv, entered_state)) + local_irq_enable(); diff = ktime_to_us(ktime_sub(time_end, time_start)); if (diff > INT_MAX) @@ -140,12 +141,14 @@ int cpuidle_idle_call(void) return 0; } - trace_cpu_idle_rcuidle(next_state, dev->cpu); - broadcast = !!(drv->states[next_state].flags & CPUIDLE_FLAG_TIMER_STOP); - if (broadcast) - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); + if (broadcast && + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu)) + return -EBUSY; + + + trace_cpu_idle_rcuidle(next_state, dev->cpu); if (cpuidle_state_is_coupled(dev, drv, next_state)) entered_state = cpuidle_enter_state_coupled(dev, drv, @@ -153,11 +156,11 @@ int cpuidle_idle_call(void) else entered_state = cpuidle_enter_state(dev, drv, next_state); + trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); + if (broadcast) clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); - trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); - /* give the governor an opportunity to reflect on the outcome */ if (cpuidle_curr_governor->reflect) cpuidle_curr_governor->reflect(dev, entered_state); diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 06dbe7c86199..136d6a283e0a 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -209,7 +209,7 @@ static void poll_idle_init(struct cpuidle_driver *drv) state->exit_latency = 0; state->target_residency = 0; state->power_usage = -1; - state->flags = 0; + state->flags = CPUIDLE_FLAG_TIME_VALID; state->enter = poll_idle; state->disabled = false; } diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index cf7f2f0e4ef5..71b523293354 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -122,9 +122,8 @@ struct menu_device { int last_state_idx; int needs_update; - unsigned int expected_us; + unsigned int next_timer_us; unsigned int predicted_us; - unsigned int exit_us; unsigned int bucket; unsigned int correction_factor[BUCKETS]; unsigned int intervals[INTERVALS]; @@ -257,7 +256,7 @@ again: stddev = int_sqrt(stddev); if (((avg > stddev * 6) && (divisor * 4 >= INTERVALS * 3)) || stddev <= 20) { - if (data->expected_us > avg) + if (data->next_timer_us > avg) data->predicted_us = avg; return; } @@ -289,7 +288,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) struct menu_device *data = &__get_cpu_var(menu_devices); int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); int i; - int multiplier; + unsigned int interactivity_req; struct timespec t; if (data->needs_update) { @@ -298,7 +297,6 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) } data->last_state_idx = 0; - data->exit_us = 0; /* Special case when user has set very strict latency requirement */ if (unlikely(latency_req == 0)) @@ -306,13 +304,11 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) /* determine the expected residency time, round up */ t = ktime_to_timespec(tick_nohz_get_sleep_length()); - data->expected_us = + data->next_timer_us = t.tv_sec * USEC_PER_SEC + t.tv_nsec / NSEC_PER_USEC; - data->bucket = which_bucket(data->expected_us); - - multiplier = performance_multiplier(); + data->bucket = which_bucket(data->next_timer_us); /* * if the correction factor is 0 (eg first time init or cpu hotplug @@ -326,17 +322,26 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) * operands are 32 bits. * Make sure to round up for half microseconds. */ - data->predicted_us = div_round64((uint64_t)data->expected_us * + data->predicted_us = div_round64((uint64_t)data->next_timer_us * data->correction_factor[data->bucket], RESOLUTION * DECAY); get_typical_interval(data); /* + * Performance multiplier defines a minimum predicted idle + * duration / latency ratio. Adjust the latency limit if + * necessary. + */ + interactivity_req = data->predicted_us / performance_multiplier(); + if (latency_req > interactivity_req) + latency_req = interactivity_req; + + /* * We want to default to C1 (hlt), not to busy polling * unless the timer is happening really really soon. */ - if (data->expected_us > 5 && + if (data->next_timer_us > 5 && !drv->states[CPUIDLE_DRIVER_STATE_START].disabled && dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable == 0) data->last_state_idx = CPUIDLE_DRIVER_STATE_START; @@ -355,11 +360,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) continue; if (s->exit_latency > latency_req) continue; - if (s->exit_latency * multiplier > data->predicted_us) - continue; data->last_state_idx = i; - data->exit_us = s->exit_latency; } return data->last_state_idx; @@ -390,36 +392,47 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) { struct menu_device *data = &__get_cpu_var(menu_devices); int last_idx = data->last_state_idx; - unsigned int last_idle_us = cpuidle_get_last_residency(dev); struct cpuidle_state *target = &drv->states[last_idx]; unsigned int measured_us; unsigned int new_factor; /* - * Ugh, this idle state doesn't support residency measurements, so we - * are basically lost in the dark. As a compromise, assume we slept - * for the whole expected time. + * Try to figure out how much time passed between entry to low + * power state and occurrence of the wakeup event. + * + * If the entered idle state didn't support residency measurements, + * we are basically lost in the dark how much time passed. + * As a compromise, assume we slept for the whole expected time. + * + * Any measured amount of time will include the exit latency. + * Since we are interested in when the wakeup begun, not when it + * was completed, we must substract the exit latency. However, if + * the measured amount of time is less than the exit latency, + * assume the state was never reached and the exit latency is 0. */ - if (unlikely(!(target->flags & CPUIDLE_FLAG_TIME_VALID))) - last_idle_us = data->expected_us; + if (unlikely(!(target->flags & CPUIDLE_FLAG_TIME_VALID))) { + /* Use timer value as is */ + measured_us = data->next_timer_us; + } else { + /* Use measured value */ + measured_us = cpuidle_get_last_residency(dev); - measured_us = last_idle_us; - - /* - * We correct for the exit latency; we are assuming here that the - * exit latency happens after the event that we're interested in. - */ - if (measured_us > data->exit_us) - measured_us -= data->exit_us; + /* Deduct exit latency */ + if (measured_us > target->exit_latency) + measured_us -= target->exit_latency; + /* Make sure our coefficients do not exceed unity */ + if (measured_us > data->next_timer_us) + measured_us = data->next_timer_us; + } /* Update our correction ratio */ new_factor = data->correction_factor[data->bucket]; new_factor -= new_factor / DECAY; - if (data->expected_us > 0 && measured_us < MAX_INTERESTING) - new_factor += RESOLUTION * measured_us / data->expected_us; + if (data->next_timer_us > 0 && measured_us < MAX_INTERESTING) + new_factor += RESOLUTION * measured_us / data->next_timer_us; else /* * we were idle so long that we count it as a perfect @@ -439,7 +452,7 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) data->correction_factor[data->bucket] = new_factor; /* update the repeating-pattern data */ - data->intervals[data->interval_ptr++] = last_idle_us; + data->intervals[data->interval_ptr++] = measured_us; if (data->interval_ptr >= INTERVALS) data->interval_ptr = 0; } diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index a0b2f7e0eedb..2042ec3656ba 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -91,26 +91,35 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) */ static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) { - int lev, prev_lev; + int lev, prev_lev, ret = 0; unsigned long cur_time; - lev = devfreq_get_freq_level(devfreq, freq); - if (lev < 0) - return lev; - cur_time = jiffies; - devfreq->time_in_state[lev] += + + prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq); + if (prev_lev < 0) { + ret = prev_lev; + goto out; + } + + devfreq->time_in_state[prev_lev] += cur_time - devfreq->last_stat_updated; - if (freq != devfreq->previous_freq) { - prev_lev = devfreq_get_freq_level(devfreq, - devfreq->previous_freq); + + lev = devfreq_get_freq_level(devfreq, freq); + if (lev < 0) { + ret = lev; + goto out; + } + + if (lev != prev_lev) { devfreq->trans_table[(prev_lev * devfreq->profile->max_state) + lev]++; devfreq->total_trans++; } - devfreq->last_stat_updated = cur_time; - return 0; +out: + devfreq->last_stat_updated = cur_time; + return ret; } /** diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 98e14ee4833c..f8bf00010d45 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -1239,9 +1239,17 @@ static u8 f15_m30h_determine_channel(struct amd64_pvt *pvt, u64 sys_addr, if (num_dcts_intlv == 2) { select = (sys_addr >> 8) & 0x3; channel = select ? 0x3 : 0; - } else if (num_dcts_intlv == 4) - channel = (sys_addr >> 8) & 0x7; - + } else if (num_dcts_intlv == 4) { + u8 intlv_addr = dct_sel_interleave_addr(pvt); + switch (intlv_addr) { + case 0x4: + channel = (sys_addr >> 8) & 0x3; + break; + case 0x5: + channel = (sys_addr >> 9) & 0x3; + break; + } + } return channel; } @@ -1799,6 +1807,17 @@ static struct amd64_family_type family_types[] = { .read_dct_pci_cfg = f10_read_dct_pci_cfg, } }, + [F16_M30H_CPUS] = { + .ctl_name = "F16h_M30h", + .f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1, + .f3_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F3, + .ops = { + .early_channel_count = f1x_early_channel_count, + .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow, + .dbam_to_cs = f16_dbam_to_chip_select, + .read_dct_pci_cfg = f10_read_dct_pci_cfg, + } + }, }; /* @@ -2578,6 +2597,11 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) break; case 0x16: + if (pvt->model == 0x30) { + fam_type = &family_types[F16_M30H_CPUS]; + pvt->ops = &family_types[F16_M30H_CPUS].ops; + break; + } fam_type = &family_types[F16_CPUS]; pvt->ops = &family_types[F16_CPUS].ops; break; @@ -2830,6 +2854,14 @@ static const struct pci_device_id amd64_pci_table[] = { .class = 0, .class_mask = 0, }, + { + .vendor = PCI_VENDOR_ID_AMD, + .device = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = 0, + .class_mask = 0, + }, {0, } }; diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index 6dc1fcc25afb..d903e0c21144 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -168,6 +168,8 @@ #define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602 #define PCI_DEVICE_ID_AMD_16H_NB_F1 0x1531 #define PCI_DEVICE_ID_AMD_16H_NB_F2 0x1532 +#define PCI_DEVICE_ID_AMD_16H_M30H_NB_F1 0x1581 +#define PCI_DEVICE_ID_AMD_16H_M30H_NB_F2 0x1582 /* * Function 1 - Address Map @@ -300,6 +302,7 @@ enum amd_families { F15_CPUS, F15_M30H_CPUS, F16_CPUS, + F16_M30H_CPUS, NUM_FAMILIES, }; diff --git a/drivers/edac/amd8111_edac.c b/drivers/edac/amd8111_edac.c index ddd890052ce2..2b63f7c2d6d2 100644 --- a/drivers/edac/amd8111_edac.c +++ b/drivers/edac/amd8111_edac.c @@ -350,6 +350,7 @@ static int amd8111_dev_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct amd8111_dev_info *dev_info = &amd8111_devices[id->driver_data]; + int ret = -ENODEV; dev_info->dev = pci_get_device(PCI_VENDOR_ID_AMD, dev_info->err_dev, NULL); @@ -359,16 +360,15 @@ static int amd8111_dev_probe(struct pci_dev *dev, "vendor %x, device %x, name %s\n", PCI_VENDOR_ID_AMD, dev_info->err_dev, dev_info->ctl_name); - return -ENODEV; + goto err; } if (pci_enable_device(dev_info->dev)) { - pci_dev_put(dev_info->dev); printk(KERN_ERR "failed to enable:" "vendor %x, device %x, name %s\n", PCI_VENDOR_ID_AMD, dev_info->err_dev, dev_info->ctl_name); - return -ENODEV; + goto err_dev_put; } /* @@ -381,8 +381,10 @@ static int amd8111_dev_probe(struct pci_dev *dev, edac_device_alloc_ctl_info(0, dev_info->ctl_name, 1, NULL, 0, 0, NULL, 0, dev_info->edac_idx); - if (!dev_info->edac_dev) - return -ENOMEM; + if (!dev_info->edac_dev) { + ret = -ENOMEM; + goto err_dev_put; + } dev_info->edac_dev->pvt_info = dev_info; dev_info->edac_dev->dev = &dev_info->dev->dev; @@ -399,8 +401,7 @@ static int amd8111_dev_probe(struct pci_dev *dev, if (edac_device_add_device(dev_info->edac_dev) > 0) { printk(KERN_ERR "failed to add edac_dev for %s\n", dev_info->ctl_name); - edac_device_free_ctl_info(dev_info->edac_dev); - return -ENODEV; + goto err_edac_free_ctl; } printk(KERN_INFO "added one edac_dev on AMD8111 " @@ -409,6 +410,13 @@ static int amd8111_dev_probe(struct pci_dev *dev, dev_info->ctl_name); return 0; + +err_edac_free_ctl: + edac_device_free_ctl_info(dev_info->edac_dev); +err_dev_put: + pci_dev_put(dev_info->dev); +err: + return ret; } static void amd8111_dev_remove(struct pci_dev *dev) @@ -437,6 +445,7 @@ static int amd8111_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct amd8111_pci_info *pci_info = &amd8111_pcis[id->driver_data]; + int ret = -ENODEV; pci_info->dev = pci_get_device(PCI_VENDOR_ID_AMD, pci_info->err_dev, NULL); @@ -446,16 +455,15 @@ static int amd8111_pci_probe(struct pci_dev *dev, "vendor %x, device %x, name %s\n", PCI_VENDOR_ID_AMD, pci_info->err_dev, pci_info->ctl_name); - return -ENODEV; + goto err; } if (pci_enable_device(pci_info->dev)) { - pci_dev_put(pci_info->dev); printk(KERN_ERR "failed to enable:" "vendor %x, device %x, name %s\n", PCI_VENDOR_ID_AMD, pci_info->err_dev, pci_info->ctl_name); - return -ENODEV; + goto err_dev_put; } /* @@ -465,8 +473,10 @@ static int amd8111_pci_probe(struct pci_dev *dev, */ pci_info->edac_idx = edac_pci_alloc_index(); pci_info->edac_dev = edac_pci_alloc_ctl_info(0, pci_info->ctl_name); - if (!pci_info->edac_dev) - return -ENOMEM; + if (!pci_info->edac_dev) { + ret = -ENOMEM; + goto err_dev_put; + } pci_info->edac_dev->pvt_info = pci_info; pci_info->edac_dev->dev = &pci_info->dev->dev; @@ -483,8 +493,7 @@ static int amd8111_pci_probe(struct pci_dev *dev, if (edac_pci_add_device(pci_info->edac_dev, pci_info->edac_idx) > 0) { printk(KERN_ERR "failed to add edac_pci for %s\n", pci_info->ctl_name); - edac_pci_free_ctl_info(pci_info->edac_dev); - return -ENODEV; + goto err_edac_free_ctl; } printk(KERN_INFO "added one edac_pci on AMD8111 " @@ -493,6 +502,13 @@ static int amd8111_pci_probe(struct pci_dev *dev, pci_info->ctl_name); return 0; + +err_edac_free_ctl: + edac_pci_free_ctl_info(pci_info->edac_dev); +err_dev_put: + pci_dev_put(pci_info->dev); +err: + return ret; } static void amd8111_pci_remove(struct pci_dev *dev) diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 92d54fa65f93..b2d71388172b 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -209,7 +209,6 @@ enum e752x_chips { */ struct e752x_pvt { - struct pci_dev *bridge_ck; struct pci_dev *dev_d0f0; struct pci_dev *dev_d0f1; u32 tolm; @@ -891,7 +890,7 @@ static void e752x_get_error_info(struct mem_ctl_info *mci, info->buf_ferr); if (info->dram_ferr) - pci_write_bits16(pvt->bridge_ck, E752X_DRAM_FERR, + pci_write_bits16(pvt->dev_d0f1, E752X_DRAM_FERR, info->dram_ferr, info->dram_ferr); pci_write_config_dword(dev, E752X_FERR_GLOBAL, @@ -936,7 +935,7 @@ static void e752x_get_error_info(struct mem_ctl_info *mci, info->buf_nerr); if (info->dram_nerr) - pci_write_bits16(pvt->bridge_ck, E752X_DRAM_NERR, + pci_write_bits16(pvt->dev_d0f1, E752X_DRAM_NERR, info->dram_nerr, info->dram_nerr); pci_write_config_dword(dev, E752X_NERR_GLOBAL, @@ -1177,38 +1176,33 @@ static void e752x_init_mem_map_table(struct pci_dev *pdev, static int e752x_get_devs(struct pci_dev *pdev, int dev_idx, struct e752x_pvt *pvt) { - struct pci_dev *dev; - - pvt->bridge_ck = pci_get_device(PCI_VENDOR_ID_INTEL, - pvt->dev_info->err_dev, pvt->bridge_ck); + pvt->dev_d0f1 = pci_get_device(PCI_VENDOR_ID_INTEL, + pvt->dev_info->err_dev, NULL); - if (pvt->bridge_ck == NULL) { - pvt->bridge_ck = pci_scan_single_device(pdev->bus, + if (pvt->dev_d0f1 == NULL) { + pvt->dev_d0f1 = pci_scan_single_device(pdev->bus, PCI_DEVFN(0, 1)); - pci_dev_get(pvt->bridge_ck); + pci_dev_get(pvt->dev_d0f1); } - if (pvt->bridge_ck == NULL) { + if (pvt->dev_d0f1 == NULL) { e752x_printk(KERN_ERR, "error reporting device not found:" "vendor %x device 0x%x (broken BIOS?)\n", PCI_VENDOR_ID_INTEL, e752x_devs[dev_idx].err_dev); return 1; } - dev = pci_get_device(PCI_VENDOR_ID_INTEL, + pvt->dev_d0f0 = pci_get_device(PCI_VENDOR_ID_INTEL, e752x_devs[dev_idx].ctl_dev, NULL); - if (dev == NULL) + if (pvt->dev_d0f0 == NULL) goto fail; - pvt->dev_d0f0 = dev; - pvt->dev_d0f1 = pci_dev_get(pvt->bridge_ck); - return 0; fail: - pci_dev_put(pvt->bridge_ck); + pci_dev_put(pvt->dev_d0f1); return 1; } @@ -1385,7 +1379,6 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) fail: pci_dev_put(pvt->dev_d0f0); pci_dev_put(pvt->dev_d0f1); - pci_dev_put(pvt->bridge_ck); edac_mc_free(mci); return -ENODEV; @@ -1419,7 +1412,6 @@ static void e752x_remove_one(struct pci_dev *pdev) pvt = (struct e752x_pvt *)mci->pvt_info; pci_dev_put(pvt->dev_d0f0); pci_dev_put(pvt->dev_d0f1); - pci_dev_put(pvt->bridge_ck); edac_mc_free(mci); } diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c index fa1326e5a4b0..022a70273ada 100644 --- a/drivers/edac/i3200_edac.c +++ b/drivers/edac/i3200_edac.c @@ -464,6 +464,8 @@ static void i3200_remove_one(struct pci_dev *pdev) iounmap(priv->window); edac_mc_free(mci); + + pci_disable_device(pdev); } static const struct pci_device_id i3200_pci_tbl[] = { diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index 36a38ee94fa8..6247d186177e 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c @@ -869,16 +869,13 @@ static void i5100_init_csrows(struct mem_ctl_info *mci) chan, rank, 0); dimm->nr_pages = npages; - if (npages) { - dimm->grain = 32; - dimm->dtype = (priv->mtr[chan][rank].width == 4) ? - DEV_X4 : DEV_X8; - dimm->mtype = MEM_RDDR2; - dimm->edac_mode = EDAC_SECDED; - snprintf(dimm->label, sizeof(dimm->label), - "DIMM%u", - i5100_rank_to_slot(mci, chan, rank)); - } + dimm->grain = 32; + dimm->dtype = (priv->mtr[chan][rank].width == 4) ? + DEV_X4 : DEV_X8; + dimm->mtype = MEM_RDDR2; + dimm->edac_mode = EDAC_SECDED; + snprintf(dimm->label, sizeof(dimm->label), "DIMM%u", + i5100_rank_to_slot(mci, chan, rank)); edac_dbg(2, "dimm channel %d, rank %d, size %ld\n", chan, rank, (long)PAGES_TO_MiB(npages)); diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c index e080cbfa8fc9..5381e98d9c0c 100644 --- a/drivers/edac/i5400_edac.c +++ b/drivers/edac/i5400_edac.c @@ -1408,6 +1408,8 @@ static void i5400_remove_one(struct pci_dev *pdev) /* retrieve references to resources, and free those resources */ i5400_put_devices(mci); + pci_disable_device(pdev); + edac_mc_free(mci); } diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index d871275196f6..8bc83b99974b 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -1708,7 +1708,7 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, const struct mce *m) { struct i7core_pvt *pvt = mci->pvt_info; - char *type, *optype, *err; + char *optype, *err; enum hw_event_mc_err_type tp_event; unsigned long error = m->status & 0x1ff0000l; bool uncorrected_error = m->mcgstatus & 1ll << 61; @@ -1721,15 +1721,11 @@ static void i7core_mce_output_error(struct mem_ctl_info *mci, u32 errnum = find_first_bit(&error, 32); if (uncorrected_error) { - if (ripv) { - type = "FATAL"; + if (ripv) tp_event = HW_EVENT_ERR_FATAL; - } else { - type = "NON_FATAL"; + else tp_event = HW_EVENT_ERR_UNCORRECTED; - } } else { - type = "CORRECTED"; tp_event = HW_EVENT_ERR_CORRECTED; } diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c index 80573df0a4d7..8d0450b9b9af 100644 --- a/drivers/edac/i82875p_edac.c +++ b/drivers/edac/i82875p_edac.c @@ -406,8 +406,6 @@ static int i82875p_probe1(struct pci_dev *pdev, int dev_idx) edac_dbg(0, "\n"); - ovrfl_pdev = pci_get_device(PCI_VEND_DEV(INTEL, 82875_6), NULL); - if (i82875p_setup_overfl_dev(pdev, &ovrfl_pdev, &ovrfl_window)) return -ENODEV; drc = readl(ovrfl_window + I82875P_DRC); diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index 30f7309446a6..51b9caa0b024 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -741,6 +741,36 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data) if (amd_filter_mce(m)) return NOTIFY_STOP; + pr_emerg(HW_ERR "%s\n", decode_error_status(m)); + + pr_emerg(HW_ERR "CPU:%d (%x:%x:%x) MC%d_STATUS[%s|%s|%s|%s|%s", + m->extcpu, + c->x86, c->x86_model, c->x86_mask, + m->bank, + ((m->status & MCI_STATUS_OVER) ? "Over" : "-"), + ((m->status & MCI_STATUS_UC) ? "UE" : "CE"), + ((m->status & MCI_STATUS_MISCV) ? "MiscV" : "-"), + ((m->status & MCI_STATUS_PCC) ? "PCC" : "-"), + ((m->status & MCI_STATUS_ADDRV) ? "AddrV" : "-")); + + if (c->x86 == 0x15 || c->x86 == 0x16) + pr_cont("|%s|%s", + ((m->status & MCI_STATUS_DEFERRED) ? "Deferred" : "-"), + ((m->status & MCI_STATUS_POISON) ? "Poison" : "-")); + + /* do the two bits[14:13] together */ + ecc = (m->status >> 45) & 0x3; + if (ecc) + pr_cont("|%sECC", ((ecc == 2) ? "C" : "U")); + + pr_cont("]: 0x%016llx\n", m->status); + + if (m->status & MCI_STATUS_ADDRV) + pr_emerg(HW_ERR "MC%d_ADDR: 0x%016llx\n", m->bank, m->addr); + + if (!fam_ops) + goto err_code; + switch (m->bank) { case 0: decode_mc0_mce(m); @@ -774,33 +804,7 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data) break; } - pr_emerg(HW_ERR "Error Status: %s\n", decode_error_status(m)); - - pr_emerg(HW_ERR "CPU:%d (%x:%x:%x) MC%d_STATUS[%s|%s|%s|%s|%s", - m->extcpu, - c->x86, c->x86_model, c->x86_mask, - m->bank, - ((m->status & MCI_STATUS_OVER) ? "Over" : "-"), - ((m->status & MCI_STATUS_UC) ? "UE" : "CE"), - ((m->status & MCI_STATUS_MISCV) ? "MiscV" : "-"), - ((m->status & MCI_STATUS_PCC) ? "PCC" : "-"), - ((m->status & MCI_STATUS_ADDRV) ? "AddrV" : "-")); - - if (c->x86 == 0x15 || c->x86 == 0x16) - pr_cont("|%s|%s", - ((m->status & MCI_STATUS_DEFERRED) ? "Deferred" : "-"), - ((m->status & MCI_STATUS_POISON) ? "Poison" : "-")); - - /* do the two bits[14:13] together */ - ecc = (m->status >> 45) & 0x3; - if (ecc) - pr_cont("|%sECC", ((ecc == 2) ? "C" : "U")); - - pr_cont("]: 0x%016llx\n", m->status); - - if (m->status & MCI_STATUS_ADDRV) - pr_emerg(HW_ERR "MC%d_ADDR: 0x%016llx\n", m->bank, m->addr); - + err_code: amd_decode_err_code(m->status & 0xffff); return NOTIFY_STOP; @@ -816,10 +820,7 @@ static int __init mce_amd_init(void) struct cpuinfo_x86 *c = &boot_cpu_data; if (c->x86_vendor != X86_VENDOR_AMD) - return 0; - - if (c->x86 < 0xf || c->x86 > 0x16) - return 0; + return -ENODEV; fam_ops = kzalloc(sizeof(struct amd_decoder_ops), GFP_KERNEL); if (!fam_ops) @@ -874,7 +875,7 @@ static int __init mce_amd_init(void) default: printk(KERN_WARNING "Huh? What family is it: 0x%x?!\n", c->x86); kfree(fam_ops); - return -EINVAL; + fam_ops = NULL; } pr_info("MCE: In-kernel MCE decoding enabled.\n"); diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index 8f9182179a7c..f4aec2e6ef56 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -357,7 +357,7 @@ int mpc85xx_pci_err_probe(struct platform_device *op) pdata->irq = irq_of_parse_and_map(op->dev.of_node, 0); res = devm_request_irq(&op->dev, pdata->irq, mpc85xx_pci_isr, - IRQF_DISABLED | IRQF_SHARED, + IRQF_SHARED, "[EDAC] PCI err", pci); if (res < 0) { printk(KERN_ERR @@ -633,7 +633,7 @@ static int mpc85xx_l2_err_probe(struct platform_device *op) if (edac_op_state == EDAC_OPSTATE_INT) { pdata->irq = irq_of_parse_and_map(op->dev.of_node, 0); res = devm_request_irq(&op->dev, pdata->irq, - mpc85xx_l2_isr, IRQF_DISABLED, + mpc85xx_l2_isr, 0, "[EDAC] L2 err", edac_dev); if (res < 0) { printk(KERN_ERR @@ -1133,7 +1133,7 @@ static int mpc85xx_mc_err_probe(struct platform_device *op) pdata->irq = irq_of_parse_and_map(op->dev.of_node, 0); res = devm_request_irq(&op->dev, pdata->irq, mpc85xx_mc_isr, - IRQF_DISABLED | IRQF_SHARED, + IRQF_SHARED, "[EDAC] MC err", mci); if (res < 0) { printk(KERN_ERR "%s: Unable to request irq %d for " diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 54e2abe671f7..347c7a1c2725 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -1263,7 +1263,7 @@ static int sbridge_get_onedevice(struct pci_dev **prev, struct pci_dev *pdev = NULL; u8 bus = 0; - sbridge_printk(KERN_INFO, + sbridge_printk(KERN_DEBUG, "Seeking for: dev %02x.%d PCI ID %04x:%04x\n", dev_descr->dev, dev_descr->func, PCI_VENDOR_ID_INTEL, dev_descr->dev_id); diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index bdb5a00f1dfa..be56e8ac95e6 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -14,10 +14,6 @@ if EXTCON comment "Extcon Device Drivers" -config OF_EXTCON - def_tristate y - depends on OF - config EXTCON_GPIO tristate "GPIO extcon support" depends on GPIOLIB diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 43eccc0e3448..bf7861ec0906 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -2,8 +2,6 @@ # Makefile for external connector class (extcon) devices # -obj-$(CONFIG_OF_EXTCON) += of_extcon.o - obj-$(CONFIG_EXTCON) += extcon-class.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 76322330cbd7..7ab21aa6eaa1 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -31,6 +31,7 @@ #include <linux/extcon.h> #include <linux/slab.h> #include <linux/sysfs.h> +#include <linux/of.h> /* * extcon_cable_name suggests the standard cable names for commonly used @@ -818,6 +819,47 @@ void extcon_dev_unregister(struct extcon_dev *edev) } EXPORT_SYMBOL_GPL(extcon_dev_unregister); +#ifdef CONFIG_OF +/* + * extcon_get_edev_by_phandle - Get the extcon device from devicetree + * @dev - instance to the given device + * @index - index into list of extcon_dev + * + * return the instance of extcon device + */ +struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) +{ + struct device_node *node; + struct extcon_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, "extcon", index); + if (!node) { + dev_err(dev, "failed to get phandle in %s node\n", + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + + edev = extcon_get_extcon_dev(node->name); + if (!edev) { + dev_err(dev, "unable to get extcon device : %s\n", node->name); + return ERR_PTR(-ENODEV); + } + + return edev; +} +#else +struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) +{ + return ERR_PTR(-ENOSYS); +} +#endif /* CONFIG_OF */ +EXPORT_SYMBOL_GPL(extcon_get_edev_by_phandle); + static int __init extcon_class_init(void) { return create_extcon_class(); diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index a63a6b21c9ad..13d522255d81 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -176,9 +176,7 @@ static int gpio_extcon_resume(struct device *dev) } #endif -static const struct dev_pm_ops gpio_extcon_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(NULL, gpio_extcon_resume) -}; +static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume); static struct platform_driver gpio_extcon_driver = { .probe = gpio_extcon_probe, diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 2aea4bcdd7f3..ddff2b72f0a8 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -271,10 +271,7 @@ static int palmas_usb_resume(struct device *dev) }; #endif -static const struct dev_pm_ops palmas_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(palmas_usb_suspend, - palmas_usb_resume) -}; +static SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume); static struct of_device_id of_palmas_match_tbl[] = { { .compatible = "ti,palmas-usb", }, diff --git a/drivers/extcon/of_extcon.c b/drivers/extcon/of_extcon.c deleted file mode 100644 index 72173ecbb311..000000000000 --- a/drivers/extcon/of_extcon.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * OF helpers for External connector (extcon) framework - * - * Copyright (C) 2013 Texas Instruments, Inc. - * Kishon Vijay Abraham I <kishon@ti.com> - * - * Copyright (C) 2013 Samsung Electronics - * 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 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/slab.h> -#include <linux/err.h> -#include <linux/extcon.h> -#include <linux/of.h> -#include <linux/of_platform.h> -#include <linux/extcon/of_extcon.h> - -/* - * of_extcon_get_extcon_dev - Get the name of extcon device from devicetree - * @dev - instance to the given device - * @index - index into list of extcon_dev - * - * return the instance of extcon device - */ -struct extcon_dev *of_extcon_get_extcon_dev(struct device *dev, int index) -{ - struct device_node *node; - struct extcon_dev *edev; - struct platform_device *extcon_parent_dev; - - if (!dev->of_node) { - dev_dbg(dev, "device does not have a device node entry\n"); - return ERR_PTR(-EINVAL); - } - - node = of_parse_phandle(dev->of_node, "extcon", index); - if (!node) { - dev_dbg(dev, "failed to get phandle in %s node\n", - dev->of_node->full_name); - return ERR_PTR(-ENODEV); - } - - extcon_parent_dev = of_find_device_by_node(node); - if (!extcon_parent_dev) { - dev_dbg(dev, "unable to find device by node\n"); - return ERR_PTR(-EPROBE_DEFER); - } - - edev = extcon_get_extcon_dev(dev_name(&extcon_parent_dev->dev)); - if (!edev) { - dev_dbg(dev, "unable to get extcon device : %s\n", - dev_name(&extcon_parent_dev->dev)); - return ERR_PTR(-ENODEV); - } - - return edev; -} -EXPORT_SYMBOL_GPL(of_extcon_get_extcon_dev); diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index de4aa409abe2..2c6d5e118ac1 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -916,7 +916,7 @@ static int lookup_existing_device(struct device *dev, void *data) old->config_rom_retries = 0; fw_notice(card, "rediscovered device %s\n", dev_name(dev)); - PREPARE_DELAYED_WORK(&old->work, fw_device_update); + old->workfn = fw_device_update; fw_schedule_device_work(old, 0); if (current_node == card->root_node) @@ -1075,7 +1075,7 @@ static void fw_device_init(struct work_struct *work) if (atomic_cmpxchg(&device->state, FW_DEVICE_INITIALIZING, FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { - PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); + device->workfn = fw_device_shutdown; fw_schedule_device_work(device, SHUTDOWN_DELAY); } else { fw_notice(card, "created device %s: GUID %08x%08x, S%d00\n", @@ -1196,13 +1196,20 @@ static void fw_device_refresh(struct work_struct *work) dev_name(&device->device), fw_rcode_string(ret)); gone: atomic_set(&device->state, FW_DEVICE_GONE); - PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); + device->workfn = fw_device_shutdown; fw_schedule_device_work(device, SHUTDOWN_DELAY); out: if (node_id == card->root_node->node_id) fw_schedule_bm_work(card, 0); } +static void fw_device_workfn(struct work_struct *work) +{ + struct fw_device *device = container_of(to_delayed_work(work), + struct fw_device, work); + device->workfn(work); +} + void fw_node_event(struct fw_card *card, struct fw_node *node, int event) { struct fw_device *device; @@ -1252,7 +1259,8 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) * power-up after getting plugged in. We schedule the * first config rom scan half a second after bus reset. */ - INIT_DELAYED_WORK(&device->work, fw_device_init); + device->workfn = fw_device_init; + INIT_DELAYED_WORK(&device->work, fw_device_workfn); fw_schedule_device_work(device, INITIAL_DELAY); break; @@ -1268,7 +1276,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) if (atomic_cmpxchg(&device->state, FW_DEVICE_RUNNING, FW_DEVICE_INITIALIZING) == FW_DEVICE_RUNNING) { - PREPARE_DELAYED_WORK(&device->work, fw_device_refresh); + device->workfn = fw_device_refresh; fw_schedule_device_work(device, device->is_local ? 0 : INITIAL_DELAY); } @@ -1283,7 +1291,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) smp_wmb(); /* update node_id before generation */ device->generation = card->generation; if (atomic_read(&device->state) == FW_DEVICE_RUNNING) { - PREPARE_DELAYED_WORK(&device->work, fw_device_update); + device->workfn = fw_device_update; fw_schedule_device_work(device, 0); } break; @@ -1308,7 +1316,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) device = node->data; if (atomic_xchg(&device->state, FW_DEVICE_GONE) == FW_DEVICE_RUNNING) { - PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); + device->workfn = fw_device_shutdown; fw_schedule_device_work(device, list_empty(&card->link) ? 0 : SHUTDOWN_DELAY); } diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 6b895986dc22..4af0a7bad7f2 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -929,8 +929,6 @@ static void fwnet_write_complete(struct fw_card *card, int rcode, if (rcode == RCODE_COMPLETE) { fwnet_transmit_packet_done(ptask); } else { - fwnet_transmit_packet_failed(ptask); - if (printk_timed_ratelimit(&j, 1000) || rcode != last_rcode) { dev_err(&ptask->dev->netdev->dev, "fwnet_write_complete failed: %x (skipped %d)\n", @@ -938,8 +936,10 @@ static void fwnet_write_complete(struct fw_card *card, int rcode, errors_skipped = 0; last_rcode = rcode; - } else + } else { errors_skipped++; + } + fwnet_transmit_packet_failed(ptask); } } diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 6f74d8d3f700..8db663219560 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -290,7 +290,6 @@ static char ohci_driver_name[] = KBUILD_MODNAME; #define QUIRK_NO_MSI 0x10 #define QUIRK_TI_SLLZ059 0x20 #define QUIRK_IR_WAKE 0x40 -#define QUIRK_PHY_LCTRL_TIMEOUT 0x80 /* In case of multiple matches in ohci_quirks[], only the first one is used. */ static const struct { @@ -303,10 +302,7 @@ static const struct { QUIRK_BE_HEADERS}, {PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_AGERE_FW643, 6, - QUIRK_PHY_LCTRL_TIMEOUT | QUIRK_NO_MSI}, - - {PCI_VENDOR_ID_ATT, PCI_ANY_ID, PCI_ANY_ID, - QUIRK_PHY_LCTRL_TIMEOUT}, + QUIRK_NO_MSI}, {PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_SB1394, PCI_ANY_ID, QUIRK_RESET_PACKET}, @@ -353,7 +349,6 @@ MODULE_PARM_DESC(quirks, "Chip quirks (default = 0" ", disable MSI = " __stringify(QUIRK_NO_MSI) ", TI SLLZ059 erratum = " __stringify(QUIRK_TI_SLLZ059) ", IR wake unreliable = " __stringify(QUIRK_IR_WAKE) - ", phy LCtrl timeout = " __stringify(QUIRK_PHY_LCTRL_TIMEOUT) ")"); #define OHCI_PARAM_DEBUG_AT_AR 1 @@ -2299,9 +2294,6 @@ static int ohci_enable(struct fw_card *card, * TI TSB82AA2 + TSB81BA3(A) cards signal LPS enabled early but * cannot actually use the phy at that time. These need tens of * millisecods pause between LPS write and first phy access too. - * - * But do not wait for 50msec on Agere/LSI cards. Their phy - * arbitration state machine may time out during such a long wait. */ reg_write(ohci, OHCI1394_HCControlSet, @@ -2309,11 +2301,8 @@ static int ohci_enable(struct fw_card *card, OHCI1394_HCControl_postedWriteEnable); flush_writes(ohci); - if (!(ohci->quirks & QUIRK_PHY_LCTRL_TIMEOUT)) + for (lps = 0, i = 0; !lps && i < 3; i++) { msleep(50); - - for (lps = 0, i = 0; !lps && i < 150; i++) { - msleep(1); lps = reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_LPS; } diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 281029daf98c..7aef911fdc71 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -146,6 +146,7 @@ struct sbp2_logical_unit { */ int generation; int retries; + work_func_t workfn; struct delayed_work work; bool has_sdev; bool blocked; @@ -864,7 +865,7 @@ static void sbp2_login(struct work_struct *work) /* set appropriate retry limit(s) in BUSY_TIMEOUT register */ sbp2_set_busy_timeout(lu); - PREPARE_DELAYED_WORK(&lu->work, sbp2_reconnect); + lu->workfn = sbp2_reconnect; sbp2_agent_reset(lu); /* This was a re-login. */ @@ -918,7 +919,7 @@ static void sbp2_login(struct work_struct *work) * If a bus reset happened, sbp2_update will have requeued * lu->work already. Reset the work from reconnect to login. */ - PREPARE_DELAYED_WORK(&lu->work, sbp2_login); + lu->workfn = sbp2_login; } static void sbp2_reconnect(struct work_struct *work) @@ -952,7 +953,7 @@ static void sbp2_reconnect(struct work_struct *work) lu->retries++ >= 5) { dev_err(tgt_dev(tgt), "failed to reconnect\n"); lu->retries = 0; - PREPARE_DELAYED_WORK(&lu->work, sbp2_login); + lu->workfn = sbp2_login; } sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5)); @@ -972,6 +973,13 @@ static void sbp2_reconnect(struct work_struct *work) sbp2_conditionally_unblock(lu); } +static void sbp2_lu_workfn(struct work_struct *work) +{ + struct sbp2_logical_unit *lu = container_of(to_delayed_work(work), + struct sbp2_logical_unit, work); + lu->workfn(work); +} + static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) { struct sbp2_logical_unit *lu; @@ -998,7 +1006,8 @@ static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry) lu->blocked = false; ++tgt->dont_block; INIT_LIST_HEAD(&lu->orb_list); - INIT_DELAYED_WORK(&lu->work, sbp2_login); + lu->workfn = sbp2_login; + INIT_DELAYED_WORK(&lu->work, sbp2_lu_workfn); list_add_tail(&lu->link, &tgt->lu_list); return 0; diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 1b5e8e46226d..7160c43c59fc 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -584,7 +584,7 @@ static struct platform_driver dcdbas_driver = { .remove = dcdbas_remove, }; -static const struct platform_device_info dcdbas_dev_info __initdata = { +static const struct platform_device_info dcdbas_dev_info __initconst = { .name = DRIVER_NAME, .id = -1, .dma_mask = DMA_BIT_MASK(32), diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index b6bffbfd3be7..ff50aeebf0d9 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -16,18 +16,6 @@ struct file_info { u64 size; }; - - - -static void efi_char16_printk(efi_system_table_t *sys_table_arg, - efi_char16_t *str) -{ - struct efi_simple_text_output_protocol *out; - - out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out; - efi_call_phys2(out->output_string, out, str); -} - static void efi_printk(efi_system_table_t *sys_table_arg, char *str) { char *s8; @@ -65,20 +53,23 @@ again: * allocation which may be in a new descriptor region. */ *map_size += sizeof(*m); - status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, - EFI_LOADER_DATA, *map_size, (void **)&m); + status = efi_call_early(allocate_pool, EFI_LOADER_DATA, + *map_size, (void **)&m); if (status != EFI_SUCCESS) goto fail; - status = efi_call_phys5(sys_table_arg->boottime->get_memory_map, - map_size, m, &key, desc_size, &desc_version); + *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_phys1(sys_table_arg->boottime->free_pool, m); + efi_call_early(free_pool, m); goto again; } if (status != EFI_SUCCESS) - efi_call_phys1(sys_table_arg->boottime->free_pool, m); + efi_call_early(free_pool, m); + if (key_ptr && status == EFI_SUCCESS) *key_ptr = key; if (desc_ver && status == EFI_SUCCESS) @@ -158,7 +149,7 @@ again: if (!max_addr) status = EFI_NOT_FOUND; else { - status = efi_call_phys4(sys_table_arg->boottime->allocate_pages, + status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &max_addr); if (status != EFI_SUCCESS) { @@ -170,8 +161,7 @@ again: *addr = max_addr; } - efi_call_phys1(sys_table_arg->boottime->free_pool, map); - + efi_call_early(free_pool, map); fail: return status; } @@ -231,7 +221,7 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, if ((start + size) > end) continue; - status = efi_call_phys4(sys_table_arg->boottime->allocate_pages, + status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &start); if (status == EFI_SUCCESS) { @@ -243,7 +233,7 @@ static efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, if (i == map_size / desc_size) status = EFI_NOT_FOUND; - efi_call_phys1(sys_table_arg->boottime->free_pool, map); + efi_call_early(free_pool, map); fail: return status; } @@ -257,7 +247,7 @@ static void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, return; nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - efi_call_phys2(sys_table_arg->boottime->free_pages, addr, nr_pages); + efi_call_early(free_pages, addr, nr_pages); } @@ -276,9 +266,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, { struct file_info *files; unsigned long file_addr; - efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; u64 file_size_total; - efi_file_io_interface_t *io; efi_file_handle_t *fh; efi_status_t status; int nr_files; @@ -319,10 +307,8 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, if (!nr_files) return EFI_SUCCESS; - status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, - EFI_LOADER_DATA, - nr_files * sizeof(*files), - (void **)&files); + status = efi_call_early(allocate_pool, EFI_LOADER_DATA, + nr_files * sizeof(*files), (void **)&files); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to alloc mem for file handle list\n"); goto fail; @@ -331,13 +317,8 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, str = cmd_line; for (i = 0; i < nr_files; i++) { struct file_info *file; - efi_file_handle_t *h; - efi_file_info_t *info; efi_char16_t filename_16[256]; - unsigned long info_sz; - efi_guid_t info_guid = EFI_FILE_INFO_ID; efi_char16_t *p; - u64 file_sz; str = strstr(str, option_string); if (!str) @@ -368,71 +349,18 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, /* Only open the volume once. */ if (!i) { - efi_boot_services_t *boottime; - - boottime = sys_table_arg->boottime; - - status = efi_call_phys3(boottime->handle_protocol, - image->device_handle, &fs_proto, - (void **)&io); - if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to handle fs_proto\n"); - goto free_files; - } - - status = efi_call_phys2(io->open_volume, io, &fh); - if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to open volume\n"); + status = efi_open_volume(sys_table_arg, image, + (void **)&fh); + if (status != EFI_SUCCESS) goto free_files; - } } - status = efi_call_phys5(fh->open, fh, &h, filename_16, - EFI_FILE_MODE_READ, (u64)0); - if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to open file: "); - efi_char16_printk(sys_table_arg, filename_16); - efi_printk(sys_table_arg, "\n"); + status = efi_file_size(sys_table_arg, fh, filename_16, + (void **)&file->handle, &file->size); + if (status != EFI_SUCCESS) goto close_handles; - } - file->handle = h; - - info_sz = 0; - status = efi_call_phys4(h->get_info, h, &info_guid, - &info_sz, NULL); - if (status != EFI_BUFFER_TOO_SMALL) { - efi_printk(sys_table_arg, "Failed to get file info size\n"); - goto close_handles; - } - -grow: - status = efi_call_phys3(sys_table_arg->boottime->allocate_pool, - EFI_LOADER_DATA, info_sz, - (void **)&info); - if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to alloc mem for file info\n"); - goto close_handles; - } - - status = efi_call_phys4(h->get_info, h, &info_guid, - &info_sz, info); - if (status == EFI_BUFFER_TOO_SMALL) { - efi_call_phys1(sys_table_arg->boottime->free_pool, - info); - goto grow; - } - - file_sz = info->file_size; - efi_call_phys1(sys_table_arg->boottime->free_pool, info); - - if (status != EFI_SUCCESS) { - efi_printk(sys_table_arg, "Failed to get file info\n"); - goto close_handles; - } - - file->size = file_sz; - file_size_total += file_sz; + file_size_total += file->size; } if (file_size_total) { @@ -468,10 +396,10 @@ grow: chunksize = EFI_READ_CHUNK_SIZE; else chunksize = size; - status = efi_call_phys3(fh->read, - files[j].handle, - &chunksize, - (void *)addr); + + status = efi_file_read(fh, files[j].handle, + &chunksize, + (void *)addr); if (status != EFI_SUCCESS) { efi_printk(sys_table_arg, "Failed to read file\n"); goto free_file_total; @@ -480,12 +408,12 @@ grow: size -= chunksize; } - efi_call_phys1(fh->close, files[j].handle); + efi_file_close(fh, files[j].handle); } } - efi_call_phys1(sys_table_arg->boottime->free_pool, files); + efi_call_early(free_pool, files); *load_addr = file_addr; *load_size = file_size_total; @@ -497,9 +425,9 @@ free_file_total: close_handles: for (k = j; k < i; k++) - efi_call_phys1(fh->close, files[k].handle); + efi_file_close(fh, files[k].handle); free_files: - efi_call_phys1(sys_table_arg->boottime->free_pool, files); + efi_call_early(free_pool, files); fail: *load_addr = 0; *load_size = 0; @@ -545,7 +473,7 @@ static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, * as possible while respecting the required alignment. */ nr_pages = round_up(alloc_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; - status = efi_call_phys4(sys_table_arg->boottime->allocate_pages, + status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, nr_pages, &efi_addr); new_addr = efi_addr; diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 4753bac65279..af20f1712337 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -233,7 +233,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab}, {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, - {NULL_GUID, NULL, 0}, + {NULL_GUID, NULL, NULL}, }; static __init int match_config_table(efi_guid_t *guid, @@ -313,5 +313,8 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) } pr_cont("\n"); early_iounmap(config_tables, efi.systab->nr_tables * sz); + + set_bit(EFI_CONFIG_TABLES, &efi.flags); + return 0; } diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index 3dc248239197..50ea412a25e6 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -227,7 +227,7 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) memcpy(&entry->var, new_var, count); err = efivar_entry_set(entry, new_var->Attributes, - new_var->DataSize, new_var->Data, false); + new_var->DataSize, new_var->Data, NULL); if (err) { printk(KERN_WARNING "efivars: set_variable() failed: status=%d\n", err); return -EIO; diff --git a/drivers/fmc/fmc-core.c b/drivers/fmc/fmc-core.c index 24d52497524d..353fc546fb08 100644 --- a/drivers/fmc/fmc-core.c +++ b/drivers/fmc/fmc-core.c @@ -99,10 +99,23 @@ static ssize_t fmc_read_eeprom(struct file *file, struct kobject *kobj, return count; } +static ssize_t fmc_write_eeprom(struct file *file, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev; + struct fmc_device *fmc; + + dev = container_of(kobj, struct device, kobj); + fmc = container_of(dev, struct fmc_device, dev); + return fmc->op->write_ee(fmc, off, buf, count); +} + static struct bin_attribute fmc_eeprom_attr = { - .attr = { .name = "eeprom", .mode = S_IRUGO, }, + .attr = { .name = "eeprom", .mode = S_IRUGO | S_IWUSR, }, .size = 8192, /* more or less standard */ .read = fmc_read_eeprom, + .write = fmc_write_eeprom, }; /* @@ -154,7 +167,7 @@ int fmc_device_register_n(struct fmc_device **devs, int n) ret = -EINVAL; break; } - if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) { + if (fmc->flags & FMC_DEVICE_NO_MEZZANINE) { dev_info(fmc->hwdev, "absent mezzanine in slot %d\n", fmc->slot_id); continue; @@ -189,9 +202,6 @@ int fmc_device_register_n(struct fmc_device **devs, int n) for (i = 0; i < n; i++) { fmc = devarray[i]; - if (fmc->flags == FMC_DEVICE_NO_MEZZANINE) - continue; /* dev_info already done above */ - fmc->nr_slots = n; /* each slot must know how many are there */ fmc->devarray = devarray; @@ -263,8 +273,6 @@ void fmc_device_unregister_n(struct fmc_device **devs, int n) kfree(devs[0]->devarray); for (i = 0; i < n; i++) { - if (devs[i]->flags == FMC_DEVICE_NO_MEZZANINE) - continue; sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); device_del(&devs[i]->dev); fmc_free_id_info(devs[i]); diff --git a/drivers/fmc/fmc-sdb.c b/drivers/fmc/fmc-sdb.c index 79adc39221ea..4603fdb74465 100644 --- a/drivers/fmc/fmc-sdb.c +++ b/drivers/fmc/fmc-sdb.c @@ -150,23 +150,36 @@ int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw, } EXPORT_SYMBOL(fmc_reprogram); +static char *__strip_trailing_space(char *buf, char *str, int len) +{ + int i = len - 1; + + memcpy(buf, str, len); + while(i >= 0 && buf[i] == ' ') + buf[i--] = '\0'; + return buf; +} + +#define __sdb_string(buf, field) ({ \ + BUILD_BUG_ON(sizeof(buf) < sizeof(field)); \ + __strip_trailing_space(buf, (void *)(field), sizeof(field)); \ + }) + static void __fmc_show_sdb_tree(const struct fmc_device *fmc, const struct sdb_array *arr) { + unsigned long base = arr->baseaddr; int i, j, n = arr->len, level = arr->level; - const struct sdb_array *ap; + char buf[64]; for (i = 0; i < n; i++) { - unsigned long base; union sdb_record *r; struct sdb_product *p; struct sdb_component *c; r = &arr->record[i]; c = &r->dev.sdb_component; p = &c->product; - base = 0; - for (ap = arr; ap; ap = ap->parent) - base += ap->baseaddr; + dev_info(&fmc->dev, "SDB: "); for (j = 0; j < level; j++) @@ -193,8 +206,8 @@ static void __fmc_show_sdb_tree(const struct fmc_device *fmc, p->name, __be64_to_cpu(c->addr_first) + base); if (IS_ERR(arr->subtree[i])) { - printk(KERN_CONT "(bridge error %li)\n", - PTR_ERR(arr->subtree[i])); + dev_info(&fmc->dev, "SDB: (bridge error %li)\n", + PTR_ERR(arr->subtree[i])); break; } __fmc_show_sdb_tree(fmc, arr->subtree[i]); @@ -203,10 +216,20 @@ static void __fmc_show_sdb_tree(const struct fmc_device *fmc, printk(KERN_CONT "integration\n"); break; case sdb_type_repo_url: - printk(KERN_CONT "repo-url\n"); + printk(KERN_CONT "Synthesis repository: %s\n", + __sdb_string(buf, r->repo_url.repo_url)); break; case sdb_type_synthesis: - printk(KERN_CONT "synthesis-info\n"); + printk(KERN_CONT "Bitstream '%s' ", + __sdb_string(buf, r->synthesis.syn_name)); + printk(KERN_CONT "synthesized %08x by %s ", + __be32_to_cpu(r->synthesis.date), + __sdb_string(buf, r->synthesis.user_name)); + printk(KERN_CONT "(%s version %x), ", + __sdb_string(buf, r->synthesis.tool_name), + __be32_to_cpu(r->synthesis.tool_version)); + printk(KERN_CONT "commit %pm\n", + r->synthesis.commit_id); break; case sdb_type_empty: printk(KERN_CONT "empty\n"); diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index acf3a36c9ebc..32982da82694 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -68,15 +68,7 @@ void __armada_drm_queue_unref_work(struct drm_device *dev, { struct armada_private *priv = dev->dev_private; - /* - * Yes, we really must jump through these hoops just to store a - * _pointer_ to something into the kfifo. This is utterly insane - * and idiotic, because it kfifo requires the _data_ pointed to by - * the pointer const, not the pointer itself. Not only that, but - * you have to pass a pointer _to_ the pointer you want stored. - */ - const struct drm_framebuffer *silly_api_alert = fb; - WARN_ON(!kfifo_put(&priv->fb_unref, &silly_api_alert)); + WARN_ON(!kfifo_put(&priv->fb_unref, fb)); schedule_work(&priv->fb_unref_work); } diff --git a/drivers/gpu/drm/bochs/Kconfig b/drivers/gpu/drm/bochs/Kconfig index c8fcf12019f0..5f8b0c2b9a44 100644 --- a/drivers/gpu/drm/bochs/Kconfig +++ b/drivers/gpu/drm/bochs/Kconfig @@ -2,6 +2,7 @@ config DRM_BOCHS tristate "DRM Support for bochs dispi vga interface (qemu stdvga)" depends on DRM && PCI select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER select FB_SYS_FILLRECT select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c index bb8f58012189..534cb89b160d 100644 --- a/drivers/gpu/drm/drm_cache.c +++ b/drivers/gpu/drm/drm_cache.c @@ -32,6 +32,12 @@ #include <drm/drmP.h> #if defined(CONFIG_X86) + +/* + * clflushopt is an unordered instruction which needs fencing with mfence or + * sfence to avoid ordering issues. For drm_clflush_page this fencing happens + * in the caller. + */ static void drm_clflush_page(struct page *page) { @@ -44,7 +50,7 @@ drm_clflush_page(struct page *page) page_virtual = kmap_atomic(page); for (i = 0; i < PAGE_SIZE; i += size) - clflush(page_virtual + i); + clflushopt(page_virtual + i); kunmap_atomic(page_virtual); } @@ -133,7 +139,7 @@ drm_clflush_virt_range(char *addr, unsigned long length) mb(); for (; addr < end; addr += boot_cpu_data.x86_clflush_size) clflush(addr); - clflush(end - 1); + clflushopt(end - 1); mb(); return; } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 7f2af9aca038..309023f12d7f 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -319,7 +319,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp, pci_dev_put(pci_dev); } if (!dev->hose) { - struct pci_bus *b = pci_bus_b(pci_root_buses.next); + struct pci_bus *b = list_entry(pci_root_buses.next, + struct pci_bus, node); if (b) dev->hose = b->sysdata; } diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 5736aaa7e86c..f7af69bcf3f4 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -468,8 +468,8 @@ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) } else { list_for_each_entry_safe(dev, tmp, &driver->legacy_dev_list, legacy_dev_list) { - drm_put_dev(dev); list_del(&dev->legacy_dev_list); + drm_put_dev(dev); } } DRM_INFO("Module unloaded\n"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 215131ab1dd2..c204b4e3356e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -172,20 +172,24 @@ static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) ret = exynos_drm_subdrv_open(dev, file); if (ret) - goto out; + goto err_file_priv_free; anon_filp = anon_inode_getfile("exynos_gem", &exynos_drm_gem_fops, NULL, 0); if (IS_ERR(anon_filp)) { ret = PTR_ERR(anon_filp); - goto out; + goto err_subdrv_close; } anon_filp->f_mode = FMODE_READ | FMODE_WRITE; file_priv->anon_filp = anon_filp; return ret; -out: + +err_subdrv_close: + exynos_drm_subdrv_close(dev, file); + +err_file_priv_free: kfree(file_priv); file->driver_priv = NULL; return ret; diff --git a/drivers/gpu/drm/gma500/Kconfig b/drivers/gpu/drm/gma500/Kconfig index 508cf99a292d..17f928ec84ea 100644 --- a/drivers/gpu/drm/gma500/Kconfig +++ b/drivers/gpu/drm/gma500/Kconfig @@ -10,7 +10,6 @@ config DRM_GMA500 # GMA500 depends on ACPI_VIDEO when ACPI is enabled, just like i915 select ACPI_VIDEO if ACPI select BACKLIGHT_CLASS_DEVICE if ACPI - select VIDEO_OUTPUT_CONTROL if ACPI select INPUT if ACPI help Say yes for an experimental 2D KMS framebuffer driver for the diff --git a/drivers/gpu/drm/gma500/mmu.c b/drivers/gpu/drm/gma500/mmu.c index 49bac41beefb..c3e67ba94446 100644 --- a/drivers/gpu/drm/gma500/mmu.c +++ b/drivers/gpu/drm/gma500/mmu.c @@ -520,7 +520,7 @@ struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers, driver->has_clflush = 0; - if (boot_cpu_has(X86_FEATURE_CLFLSH)) { + if (boot_cpu_has(X86_FEATURE_CLFLUSH)) { uint32_t tfms, misc, cap0, cap4, clflush_size; /* diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 73ed59eff139..bea2d67196fb 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -14,7 +14,6 @@ config DRM_I915 # but for select to work, need to select ACPI_VIDEO's dependencies, ick select BACKLIGHT_LCD_SUPPORT if ACPI select BACKLIGHT_CLASS_DEVICE if ACPI - select VIDEO_OUTPUT_CONTROL if ACPI select INPUT if ACPI select ACPI_VIDEO if ACPI select ACPI_BUTTON if ACPI diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 04f1f02c4019..ec7bb0fc71bc 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -403,7 +403,7 @@ MODULE_DEVICE_TABLE(pci, pciidlist); void intel_detect_pch(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct pci_dev *pch; + struct pci_dev *pch = NULL; /* In all current cases, num_pipes is equivalent to the PCH_NOP setting * (which really amounts to a PCH but no South Display). @@ -424,12 +424,9 @@ void intel_detect_pch(struct drm_device *dev) * all the ISA bridge devices and check for the first match, instead * of only checking the first one. */ - pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, NULL); - while (pch) { - struct pci_dev *curr = pch; + while ((pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, pch))) { if (pch->vendor == PCI_VENDOR_ID_INTEL) { - unsigned short id; - id = pch->device & INTEL_PCH_DEVICE_ID_MASK; + unsigned short id = pch->device & INTEL_PCH_DEVICE_ID_MASK; dev_priv->pch_id = id; if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) { @@ -461,18 +458,16 @@ void intel_detect_pch(struct drm_device *dev) DRM_DEBUG_KMS("Found LynxPoint LP PCH\n"); WARN_ON(!IS_HASWELL(dev)); WARN_ON(!IS_ULT(dev)); - } else { - goto check_next; - } - pci_dev_put(pch); + } else + continue; + break; } -check_next: - pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, curr); - pci_dev_put(curr); } if (!pch) - DRM_DEBUG_KMS("No PCH found?\n"); + DRM_DEBUG_KMS("No PCH found.\n"); + + pci_dev_put(pch); } bool i915_semaphore_is_enabled(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 40a2b36b276b..d278be110805 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -842,7 +842,7 @@ void i915_gem_suspend_gtt_mappings(struct drm_device *dev) dev_priv->gtt.base.clear_range(&dev_priv->gtt.base, dev_priv->gtt.base.start / PAGE_SIZE, dev_priv->gtt.base.total / PAGE_SIZE, - false); + true); } void i915_gem_restore_gtt_mappings(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 1a24e84f2315..28d24caa49f3 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -82,9 +82,22 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) r = devm_request_mem_region(dev->dev, base, dev_priv->gtt.stolen_size, "Graphics Stolen Memory"); if (r == NULL) { - DRM_ERROR("conflict detected with stolen region: [0x%08x - 0x%08x]\n", - base, base + (uint32_t)dev_priv->gtt.stolen_size); - base = 0; + /* + * One more attempt but this time requesting region from + * base + 1, as we have seen that this resolves the region + * conflict with the PCI Bus. + * This is a BIOS w/a: Some BIOS wrap stolen in the root + * PCI bus, but have an off-by-one error. Hence retry the + * reservation starting from 1 instead of 0. + */ + r = devm_request_mem_region(dev->dev, base + 1, + dev_priv->gtt.stolen_size - 1, + "Graphics Stolen Memory"); + if (r == NULL) { + DRM_ERROR("conflict detected with stolen region: [0x%08x - 0x%08x]\n", + base, base + (uint32_t)dev_priv->gtt.stolen_size); + base = 0; + } } return base; @@ -201,6 +214,13 @@ int i915_gem_init_stolen(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int bios_reserved = 0; +#ifdef CONFIG_INTEL_IOMMU + if (intel_iommu_gfx_mapped) { + DRM_INFO("DMAR active, disabling use of stolen memory\n"); + return 0; + } +#endif + if (dev_priv->gtt.stolen_size == 0) return 0; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 9fec71175571..d554169ac592 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -618,33 +618,25 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) /* raw reads, only for fast reads of display block, no need for forcewake etc. */ #define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__)) -#define __raw_i915_read16(dev_priv__, reg__) readw((dev_priv__)->regs + (reg__)) static bool ilk_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t status; - - if (INTEL_INFO(dev)->gen < 7) { - status = pipe == PIPE_A ? - DE_PIPEA_VBLANK : - DE_PIPEB_VBLANK; + int reg; + + if (INTEL_INFO(dev)->gen >= 8) { + status = GEN8_PIPE_VBLANK; + reg = GEN8_DE_PIPE_ISR(pipe); + } else if (INTEL_INFO(dev)->gen >= 7) { + status = DE_PIPE_VBLANK_IVB(pipe); + reg = DEISR; } else { - switch (pipe) { - default: - case PIPE_A: - status = DE_PIPEA_VBLANK_IVB; - break; - case PIPE_B: - status = DE_PIPEB_VBLANK_IVB; - break; - case PIPE_C: - status = DE_PIPEC_VBLANK_IVB; - break; - } + status = DE_PIPE_VBLANK(pipe); + reg = DEISR; } - return __raw_i915_read32(dev_priv, DEISR) & status; + return __raw_i915_read32(dev_priv, reg) & status; } static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, @@ -702,7 +694,28 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, else position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3; - if (HAS_PCH_SPLIT(dev)) { + if (HAS_DDI(dev)) { + /* + * On HSW HDMI outputs there seems to be a 2 line + * difference, whereas eDP has the normal 1 line + * difference that earlier platforms have. External + * DP is unknown. For now just check for the 2 line + * difference case on all output types on HSW+. + * + * This might misinterpret the scanline counter being + * one line too far along on eDP, but that's less + * dangerous than the alternative since that would lead + * the vblank timestamp code astray when it sees a + * scanline count before vblank_start during a vblank + * interrupt. + */ + in_vbl = ilk_pipe_in_vblank_locked(dev, pipe); + if ((in_vbl && (position == vbl_start - 2 || + position == vbl_start - 1)) || + (!in_vbl && (position == vbl_end - 2 || + position == vbl_end - 1))) + position = (position + 2) % vtotal; + } else if (HAS_PCH_SPLIT(dev)) { /* * The scanline counter increments at the leading edge * of hsync, ie. it completely misses the active portion @@ -2769,10 +2782,9 @@ static void ibx_irq_postinstall(struct drm_device *dev) return; if (HAS_PCH_IBX(dev)) { - mask = SDE_GMBUS | SDE_AUX_MASK | SDE_TRANSB_FIFO_UNDER | - SDE_TRANSA_FIFO_UNDER | SDE_POISON; + mask = SDE_GMBUS | SDE_AUX_MASK | SDE_POISON; } else { - mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT | SDE_ERROR_CPT; + mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT; I915_WRITE(SERR_INT, I915_READ(SERR_INT)); } @@ -2832,20 +2844,19 @@ static int ironlake_irq_postinstall(struct drm_device *dev) display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | DE_PCH_EVENT_IVB | DE_PLANEC_FLIP_DONE_IVB | DE_PLANEB_FLIP_DONE_IVB | - DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB | - DE_ERR_INT_IVB); + DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB); extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB | - DE_PIPEA_VBLANK_IVB); + DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB); I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT)); } else { display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | DE_AUX_CHANNEL_A | - DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN | DE_PIPEB_CRC_DONE | DE_PIPEA_CRC_DONE | DE_POISON); - extra_mask = DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT; + extra_mask = DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT | + DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN; } dev_priv->irq_mask = ~display_mask; @@ -2961,9 +2972,9 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) struct drm_device *dev = dev_priv->dev; uint32_t de_pipe_masked = GEN8_PIPE_FLIP_DONE | GEN8_PIPE_CDCLK_CRC_DONE | - GEN8_PIPE_FIFO_UNDERRUN | GEN8_DE_PIPE_IRQ_FAULT_ERRORS; - uint32_t de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK; + uint32_t de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK | + GEN8_PIPE_FIFO_UNDERRUN; int pipe; dev_priv->de_irq_mask[PIPE_A] = ~de_pipe_masked; dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_masked; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index e06b9e017d6b..234ac5f7bc5a 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1244,6 +1244,7 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); + ironlake_edp_panel_vdd_on(intel_dp); ironlake_edp_panel_off(intel_dp); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4c1672809493..9b8a7c7ea7fc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1092,12 +1092,12 @@ static void assert_cursor(struct drm_i915_private *dev_priv, struct drm_device *dev = dev_priv->dev; bool cur_state; - if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) - cur_state = I915_READ(CURCNTR_IVB(pipe)) & CURSOR_MODE; - else if (IS_845G(dev) || IS_I865G(dev)) + if (IS_845G(dev) || IS_I865G(dev)) cur_state = I915_READ(_CURACNTR) & CURSOR_ENABLE; - else + else if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) cur_state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE; + else + cur_state = I915_READ(CURCNTR_IVB(pipe)) & CURSOR_MODE; WARN(cur_state != state, "cursor on pipe %c assertion failure (expected %s, current %s)\n", diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 57552eb386b0..2688f6d64bb9 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1249,17 +1249,24 @@ void ironlake_edp_panel_off(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Turn eDP power off\n"); + WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n"); + pp = ironlake_get_pp_control(intel_dp); /* We need to switch off panel power _and_ force vdd, for otherwise some * panels get very unhappy and cease to work. */ - pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_BLC_ENABLE); + pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD | PANEL_POWER_RESET | EDP_BLC_ENABLE); pp_ctrl_reg = _pp_ctrl_reg(intel_dp); I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); + intel_dp->want_panel_vdd = false; + ironlake_wait_panel_off(intel_dp); + + /* We got a reference when we enabled the VDD. */ + intel_runtime_pm_put(dev_priv); } void ironlake_edp_backlight_on(struct intel_dp *intel_dp) @@ -1639,7 +1646,7 @@ static void intel_edp_psr_enable_source(struct intel_dp *intel_dp) val |= EDP_PSR_LINK_DISABLE; I915_WRITE(EDP_PSR_CTL(dev), val | - IS_BROADWELL(dev) ? 0 : link_entry_time | + (IS_BROADWELL(dev) ? 0 : link_entry_time) | max_sleep_time << EDP_PSR_MAX_SLEEP_TIME_SHIFT | idle_frames << EDP_PSR_IDLE_FRAME_SHIFT | EDP_PSR_ENABLE); @@ -1784,6 +1791,7 @@ static void intel_disable_dp(struct intel_encoder *encoder) /* Make sure the panel is off before trying to change the mode. But also * ensure that we have vdd while we switch off the panel. */ + ironlake_edp_panel_vdd_on(intel_dp); ironlake_edp_backlight_off(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF); ironlake_edp_panel_off(intel_dp); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 6db0d9d17f47..ee3181ebcc92 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -845,7 +845,7 @@ static int hdmi_portclock_limit(struct intel_hdmi *hdmi) { struct drm_device *dev = intel_hdmi_to_dev(hdmi); - if (IS_G4X(dev)) + if (!hdmi->has_hdmi_sink || IS_G4X(dev)) return 165000; else if (IS_HASWELL(dev) || INTEL_INFO(dev)->gen >= 8) return 300000; @@ -899,8 +899,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, * outputs. We also need to check that the higher clock still fits * within limits. */ - if (pipe_config->pipe_bpp > 8*3 && clock_12bpc <= portclock_limit - && HAS_PCH_SPLIT(dev)) { + if (pipe_config->pipe_bpp > 8*3 && intel_hdmi->has_hdmi_sink && + clock_12bpc <= portclock_limit && HAS_PCH_SPLIT(dev)) { DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n"); desired_bpp = 12*3; diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 350de359123a..079ea38f14d9 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -698,7 +698,7 @@ static void i9xx_enable_backlight(struct intel_connector *connector) freq /= 0xff; ctl = freq << 17; - if (IS_GEN2(dev) && panel->backlight.combination_mode) + if (panel->backlight.combination_mode) ctl |= BLM_LEGACY_MODE; if (IS_PINEVIEW(dev) && panel->backlight.active_low_pwm) ctl |= BLM_POLARITY_PNV; @@ -979,7 +979,7 @@ static int i9xx_setup_backlight(struct intel_connector *connector) ctl = I915_READ(BLC_PWM_CTL); - if (IS_GEN2(dev)) + if (IS_GEN2(dev) || IS_I915GM(dev) || IS_I945GM(dev)) panel->backlight.combination_mode = ctl & BLM_LEGACY_MODE; if (IS_PINEVIEW(dev)) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index d77cc81900f9..e1fc35a72656 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3493,6 +3493,8 @@ static void valleyview_setup_pctx(struct drm_device *dev) u32 pcbr; int pctx_size = 24*1024; + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + pcbr = I915_READ(VLV_PCBR); if (pcbr) { /* BIOS set it up already, grab the pre-alloc'd space */ @@ -3542,8 +3544,6 @@ static void valleyview_enable_rps(struct drm_device *dev) I915_WRITE(GTFIFODBG, gtfifodbg); } - valleyview_setup_pctx(dev); - /* If VLV, Forcewake all wells, else re-direct to regular path */ gen6_gt_force_wake_get(dev_priv, FORCEWAKE_ALL); @@ -4395,6 +4395,8 @@ void intel_enable_gt_powersave(struct drm_device *dev) ironlake_enable_rc6(dev); intel_init_emon(dev); } else if (IS_GEN6(dev) || IS_GEN7(dev)) { + if (IS_VALLEYVIEW(dev)) + valleyview_setup_pctx(dev); /* * PCU communication is slow and this doesn't need to be * done at any specific time, so do this out of our fast path diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index 7cf787d697b1..637c29a33127 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -11,7 +11,7 @@ config DRM_NOUVEAU select FB select FRAMEBUFFER_CONSOLE if !EXPERT select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT - select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL && INPUT + select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && INPUT select X86_PLATFORM_DEVICES if ACPI && X86 select ACPI_WMI if ACPI && X86 select MXM_WMI if ACPI && X86 @@ -19,7 +19,6 @@ config DRM_NOUVEAU # Similar to i915, we need to select ACPI_VIDEO and it's dependencies select BACKLIGHT_LCD_SUPPORT if ACPI && X86 select BACKLIGHT_CLASS_DEVICE if ACPI && X86 - select VIDEO_OUTPUT_CONTROL if ACPI && X86 select INPUT if ACPI && X86 select THERMAL if ACPI && X86 select ACPI_VIDEO if ACPI && X86 diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 89c484d8ac26..4ee702ac8907 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -866,13 +866,16 @@ static int nouveau_pmops_runtime_suspend(struct device *dev) struct drm_device *drm_dev = pci_get_drvdata(pdev); int ret; - if (nouveau_runtime_pm == 0) - return -EINVAL; + if (nouveau_runtime_pm == 0) { + pm_runtime_forbid(dev); + return -EBUSY; + } /* are we optimus enabled? */ if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) { DRM_DEBUG_DRIVER("failing to power off - not optimus\n"); - return -EINVAL; + pm_runtime_forbid(dev); + return -EBUSY; } nv_debug_level(SILENT); @@ -923,12 +926,15 @@ static int nouveau_pmops_runtime_idle(struct device *dev) struct nouveau_drm *drm = nouveau_drm(drm_dev); struct drm_crtc *crtc; - if (nouveau_runtime_pm == 0) + if (nouveau_runtime_pm == 0) { + pm_runtime_forbid(dev); return -EBUSY; + } /* are we optimus enabled? */ if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) { DRM_DEBUG_DRIVER("failing to power off - not optimus\n"); + pm_runtime_forbid(dev); return -EBUSY; } diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 2cec2ab02f80..607dc14d195e 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -1314,7 +1314,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t } if (is_dp) args.v5.ucLaneNum = dp_lane_count; - else if (radeon_encoder->pixel_clock > 165000) + else if (radeon_dig_monitor_is_duallink(encoder, radeon_encoder->pixel_clock)) args.v5.ucLaneNum = 8; else args.v5.ucLaneNum = 4; diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index e6419ca7cd37..bbb17841a9e5 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3046,7 +3046,7 @@ static u32 cik_create_bitmask(u32 bit_width) } /** - * cik_select_se_sh - select which SE, SH to address + * cik_get_rb_disabled - computes the mask of disabled RBs * * @rdev: radeon_device pointer * @max_rb_num: max RBs (render backends) for the asic @@ -4134,8 +4134,11 @@ static void cik_cp_compute_enable(struct radeon_device *rdev, bool enable) { if (enable) WREG32(CP_MEC_CNTL, 0); - else + else { WREG32(CP_MEC_CNTL, (MEC_ME1_HALT | MEC_ME2_HALT)); + rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false; + rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = false; + } udelay(50); } @@ -7902,7 +7905,8 @@ int cik_resume(struct radeon_device *rdev) /* init golden registers */ cik_init_golden_registers(rdev); - radeon_pm_resume(rdev); + if (rdev->pm.pm_method == PM_METHOD_DPM) + radeon_pm_resume(rdev); rdev->accel_working = true; r = cik_startup(rdev); diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c index 1ecb3f1070e3..94626ea90fa5 100644 --- a/drivers/gpu/drm/radeon/cik_sdma.c +++ b/drivers/gpu/drm/radeon/cik_sdma.c @@ -264,6 +264,8 @@ static void cik_sdma_gfx_stop(struct radeon_device *rdev) WREG32(SDMA0_GFX_RB_CNTL + reg_offset, rb_cntl); WREG32(SDMA0_GFX_IB_CNTL + reg_offset, 0); } + rdev->ring[R600_RING_TYPE_DMA_INDEX].ready = false; + rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX].ready = false; } /** @@ -291,6 +293,11 @@ void cik_sdma_enable(struct radeon_device *rdev, bool enable) u32 me_cntl, reg_offset; int i; + if (enable == false) { + cik_sdma_gfx_stop(rdev); + cik_sdma_rlc_stop(rdev); + } + for (i = 0; i < 2; i++) { if (i == 0) reg_offset = SDMA0_REGISTER_OFFSET; @@ -420,10 +427,6 @@ static int cik_sdma_load_microcode(struct radeon_device *rdev) if (!rdev->sdma_fw) return -EINVAL; - /* stop the gfx rings and rlc compute queues */ - cik_sdma_gfx_stop(rdev); - cik_sdma_rlc_stop(rdev); - /* halt the MEs */ cik_sdma_enable(rdev, false); @@ -492,9 +495,6 @@ int cik_sdma_resume(struct radeon_device *rdev) */ void cik_sdma_fini(struct radeon_device *rdev) { - /* stop the gfx rings and rlc compute queues */ - cik_sdma_gfx_stop(rdev); - cik_sdma_rlc_stop(rdev); /* halt the MEs */ cik_sdma_enable(rdev, false); radeon_ring_fini(rdev, &rdev->ring[R600_RING_TYPE_DMA_INDEX]); diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 8a2c010b7dc5..27b0ff16082e 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -5299,7 +5299,8 @@ int evergreen_resume(struct radeon_device *rdev) /* init golden registers */ evergreen_init_golden_registers(rdev); - radeon_pm_resume(rdev); + if (rdev->pm.pm_method == PM_METHOD_DPM) + radeon_pm_resume(rdev); rdev->accel_working = true; r = evergreen_startup(rdev); diff --git a/drivers/gpu/drm/radeon/evergreen_smc.h b/drivers/gpu/drm/radeon/evergreen_smc.h index 76ada8cfe902..3a03ba37d043 100644 --- a/drivers/gpu/drm/radeon/evergreen_smc.h +++ b/drivers/gpu/drm/radeon/evergreen_smc.h @@ -57,7 +57,7 @@ typedef struct SMC_Evergreen_MCRegisters SMC_Evergreen_MCRegisters; #define EVERGREEN_SMC_FIRMWARE_HEADER_LOCATION 0x100 -#define EVERGREEN_SMC_FIRMWARE_HEADER_softRegisters 0x0 +#define EVERGREEN_SMC_FIRMWARE_HEADER_softRegisters 0x8 #define EVERGREEN_SMC_FIRMWARE_HEADER_stateTable 0xC #define EVERGREEN_SMC_FIRMWARE_HEADER_mcRegisterTable 0x20 diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index ea932ac66fc6..bf6300cfd62d 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -2105,7 +2105,8 @@ int cayman_resume(struct radeon_device *rdev) /* init golden registers */ ni_init_golden_registers(rdev); - radeon_pm_resume(rdev); + if (rdev->pm.pm_method == PM_METHOD_DPM) + radeon_pm_resume(rdev); rdev->accel_working = true; r = cayman_startup(rdev); diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index ef024ce3f7cc..3cc78bb66042 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -3942,8 +3942,6 @@ int r100_resume(struct radeon_device *rdev) /* Initialize surface registers */ radeon_surface_init(rdev); - radeon_pm_resume(rdev); - rdev->accel_working = true; r = r100_startup(rdev); if (r) { diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 7c63ef840e86..0b658b34b33a 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -1430,8 +1430,6 @@ int r300_resume(struct radeon_device *rdev) /* Initialize surface registers */ radeon_surface_init(rdev); - radeon_pm_resume(rdev); - rdev->accel_working = true; r = r300_startup(rdev); if (r) { diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c index 3768aab2710b..802b19220a21 100644 --- a/drivers/gpu/drm/radeon/r420.c +++ b/drivers/gpu/drm/radeon/r420.c @@ -325,8 +325,6 @@ int r420_resume(struct radeon_device *rdev) /* Initialize surface registers */ radeon_surface_init(rdev); - radeon_pm_resume(rdev); - rdev->accel_working = true; r = r420_startup(rdev); if (r) { diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c index e209eb75024f..98d6053c36c6 100644 --- a/drivers/gpu/drm/radeon/r520.c +++ b/drivers/gpu/drm/radeon/r520.c @@ -240,8 +240,6 @@ int r520_resume(struct radeon_device *rdev) /* Initialize surface registers */ radeon_surface_init(rdev); - radeon_pm_resume(rdev); - rdev->accel_working = true; r = r520_startup(rdev); if (r) { diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index cdbc4171fe73..647ef4079217 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -2968,7 +2968,8 @@ int r600_resume(struct radeon_device *rdev) /* post card */ atom_asic_init(rdev->mode_info.atom_context); - radeon_pm_resume(rdev); + if (rdev->pm.pm_method == PM_METHOD_DPM) + radeon_pm_resume(rdev); rdev->accel_working = true; r = r600_startup(rdev); diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index b012cbbc3ed5..044bc98fb459 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1521,13 +1521,16 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon) if (r) DRM_ERROR("ib ring test failed (%d).\n", r); - if (rdev->pm.dpm_enabled) { + if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { /* do dpm late init */ r = radeon_pm_late_init(rdev); if (r) { rdev->pm.dpm_enabled = false; DRM_ERROR("radeon_pm_late_init failed, disabling dpm\n"); } + } else { + /* resume old pm late */ + radeon_pm_resume(rdev); } radeon_restore_bios_scratch_regs(rdev); diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 84a1bbb75f91..f633c2782170 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -403,11 +403,15 @@ static int radeon_pmops_runtime_suspend(struct device *dev) struct drm_device *drm_dev = pci_get_drvdata(pdev); int ret; - if (radeon_runtime_pm == 0) - return -EINVAL; + if (radeon_runtime_pm == 0) { + pm_runtime_forbid(dev); + return -EBUSY; + } - if (radeon_runtime_pm == -1 && !radeon_is_px()) - return -EINVAL; + if (radeon_runtime_pm == -1 && !radeon_is_px()) { + pm_runtime_forbid(dev); + return -EBUSY; + } drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; drm_kms_helper_poll_disable(drm_dev); @@ -456,12 +460,15 @@ static int radeon_pmops_runtime_idle(struct device *dev) struct drm_device *drm_dev = pci_get_drvdata(pdev); struct drm_crtc *crtc; - if (radeon_runtime_pm == 0) + if (radeon_runtime_pm == 0) { + pm_runtime_forbid(dev); return -EBUSY; + } /* are we PX enabled? */ if (radeon_runtime_pm == -1 && !radeon_is_px()) { DRM_DEBUG_DRIVER("failing to power off - not px\n"); + pm_runtime_forbid(dev); return -EBUSY; } diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 2aecd6dc2610..66ed3ea71440 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -33,6 +33,13 @@ #include <linux/vga_switcheroo.h> #include <linux/slab.h> #include <linux/pm_runtime.h> + +#if defined(CONFIG_VGA_SWITCHEROO) +bool radeon_is_px(void); +#else +static inline bool radeon_is_px(void) { return false; } +#endif + /** * radeon_driver_unload_kms - Main unload function for KMS. * @@ -130,7 +137,8 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags) "Error during ACPI methods call\n"); } - if (radeon_runtime_pm != 0) { + if ((radeon_runtime_pm == 1) || + ((radeon_runtime_pm == -1) && radeon_is_px())) { pm_runtime_use_autosuspend(dev->dev); pm_runtime_set_autosuspend_delay(dev->dev, 5000); pm_runtime_set_active(dev->dev); diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 77f5b0c3edb8..040a2a10ea17 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -714,6 +714,9 @@ int radeon_ttm_init(struct radeon_device *rdev) DRM_ERROR("Failed initializing VRAM heap.\n"); return r; } + /* Change the size here instead of the init above so only lpfn is affected */ + radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); + r = radeon_bo_create(rdev, 256 * 1024, PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->stollen_vga_memory); @@ -935,7 +938,7 @@ static ssize_t radeon_ttm_gtt_read(struct file *f, char __user *buf, while (size) { loff_t p = *pos / PAGE_SIZE; unsigned off = *pos & ~PAGE_MASK; - ssize_t cur_size = min(size, PAGE_SIZE - off); + size_t cur_size = min_t(size_t, size, PAGE_SIZE - off); struct page *page; void *ptr; diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index b5c2369cda2f..130d5cc50d43 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -474,8 +474,6 @@ int rs400_resume(struct radeon_device *rdev) /* Initialize surface registers */ radeon_surface_init(rdev); - radeon_pm_resume(rdev); - rdev->accel_working = true; r = rs400_startup(rdev); if (r) { diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index fdcde7693032..72d3616de08e 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -1048,8 +1048,6 @@ int rs600_resume(struct radeon_device *rdev) /* Initialize surface registers */ radeon_surface_init(rdev); - radeon_pm_resume(rdev); - rdev->accel_working = true; r = rs600_startup(rdev); if (r) { diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c index 35950738bd5e..3462b64369bf 100644 --- a/drivers/gpu/drm/radeon/rs690.c +++ b/drivers/gpu/drm/radeon/rs690.c @@ -756,8 +756,6 @@ int rs690_resume(struct radeon_device *rdev) /* Initialize surface registers */ radeon_surface_init(rdev); - radeon_pm_resume(rdev); - rdev->accel_working = true; r = rs690_startup(rdev); if (r) { diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index 98e8138ff779..237dd29d9f1c 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -586,8 +586,6 @@ int rv515_resume(struct radeon_device *rdev) /* Initialize surface registers */ radeon_surface_init(rdev); - radeon_pm_resume(rdev); - rdev->accel_working = true; r = rv515_startup(rdev); if (r) { diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 4e37a42305d8..fef310773aad 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -1811,7 +1811,8 @@ int rv770_resume(struct radeon_device *rdev) /* init golden registers */ rv770_init_golden_registers(rdev); - radeon_pm_resume(rdev); + if (rdev->pm.pm_method == PM_METHOD_DPM) + radeon_pm_resume(rdev); rdev->accel_working = true; r = rv770_startup(rdev); diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 83578324e5d1..9a124d0608b3 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -6618,7 +6618,8 @@ int si_resume(struct radeon_device *rdev) /* init golden registers */ si_init_golden_registers(rdev); - radeon_pm_resume(rdev); + if (rdev->pm.pm_method == PM_METHOD_DPM) + radeon_pm_resume(rdev); rdev->accel_working = true; r = si_startup(rdev); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index a06651309388..214b7992a3aa 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -351,9 +351,11 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, moved: if (bo->evicted) { - ret = bdev->driver->invalidate_caches(bdev, bo->mem.placement); - if (ret) - pr_err("Can not flush read caches\n"); + if (bdev->driver->invalidate_caches) { + ret = bdev->driver->invalidate_caches(bdev, bo->mem.placement); + if (ret) + pr_err("Can not flush read caches\n"); + } bo->evicted = false; } diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 801231c9ae48..0ce48e5a9cb4 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -339,11 +339,13 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, vma->vm_private_data = bo; /* - * PFNMAP is faster than MIXEDMAP due to reduced page - * administration. So use MIXEDMAP only if private VMA, where - * we need to support COW. + * We'd like to use VM_PFNMAP on shared mappings, where + * (vma->vm_flags & VM_SHARED) != 0, for performance reasons, + * but for some reason VM_PFNMAP + x86 PAT + write-combine is very + * bad for performance. Until that has been sorted out, use + * VM_MIXEDMAP on all mappings. See freedesktop.org bug #75719 */ - vma->vm_flags |= (vma->vm_flags & VM_SHARED) ? VM_PFNMAP : VM_MIXEDMAP; + vma->vm_flags |= VM_MIXEDMAP; vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; return 0; out_unref: @@ -359,7 +361,7 @@ int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo) vma->vm_ops = &ttm_bo_vm_ops; vma->vm_private_data = ttm_bo_reference(bo); - vma->vm_flags |= (vma->vm_flags & VM_SHARED) ? VM_PFNMAP : VM_MIXEDMAP; + vma->vm_flags |= VM_MIXEDMAP; vma->vm_flags |= VM_IO | VM_DONTEXPAND; return 0; } diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index 8d67b943ac05..0394811251bd 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -177,8 +177,10 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj) if (obj->vmapping) udl_gem_vunmap(obj); - if (gem_obj->import_attach) + if (gem_obj->import_attach) { drm_prime_gem_destroy(gem_obj, obj->sg); + put_device(gem_obj->dev->dev); + } if (obj->pages) udl_gem_put_pages(obj); @@ -256,9 +258,12 @@ struct drm_gem_object *udl_gem_prime_import(struct drm_device *dev, int ret; /* need to attach */ + get_device(dev->dev); attach = dma_buf_attach(dma_buf, dev->dev); - if (IS_ERR(attach)) + if (IS_ERR(attach)) { + put_device(dev->dev); return ERR_CAST(attach); + } get_dma_buf(dma_buf); @@ -282,6 +287,6 @@ fail_unmap: fail_detach: dma_buf_detach(dma_buf, attach); dma_buf_put(dma_buf); - + put_device(dev->dev); return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 82468d902915..e7af580ab977 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -830,6 +830,24 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, if (unlikely(ret != 0)) goto out_unlock; + /* + * A gb-aware client referencing a shared surface will + * expect a backup buffer to be present. + */ + if (dev_priv->has_mob && req->shareable) { + uint32_t backup_handle; + + ret = vmw_user_dmabuf_alloc(dev_priv, tfile, + res->backup_size, + true, + &backup_handle, + &res->backup); + if (unlikely(ret != 0)) { + vmw_resource_unreference(&res); + goto out_unlock; + } + } + tmp = vmw_resource_reference(&srf->res); ret = ttm_prime_object_init(tfile, res->backup_size, &user_srf->prime, req->shareable, VMW_RES_SURFACE, diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index befe0e336471..24883b4d1a49 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -43,6 +43,7 @@ #define G25_REV_MIN 0x22 #define G27_REV_MAJ 0x12 #define G27_REV_MIN 0x38 +#define G27_2_REV_MIN 0x39 #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) @@ -130,6 +131,7 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = { {DFP_REV_MAJ, DFP_REV_MIN, &native_dfp}, /* Driving Force Pro */ {G25_REV_MAJ, G25_REV_MIN, &native_g25}, /* G25 */ {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ + {G27_REV_MAJ, G27_2_REV_MIN, &native_g27}, /* G27 v2 */ }; /* Recalculates X axis value accordingly to currently selected range */ diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index 7ed828056414..91fab975063c 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -624,7 +624,8 @@ static int pcmidi_snd_initialise(struct pcmidi_snd *pm) /* Setup sound card */ - err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); + err = snd_card_new(&pm->pk->hdev->dev, index[dev], id[dev], + THIS_MODULE, 0, &card); if (err < 0) { pk_error("failed to create pc-midi sound card\n"); err = -ENOMEM; @@ -660,8 +661,6 @@ static int pcmidi_snd_initialise(struct pcmidi_snd *pm) snd_rawmidi_set_ops(rwmidi, SNDRV_RAWMIDI_STREAM_INPUT, &pcmidi_in_ops); - snd_card_set_dev(card, &pm->pk->hdev->dev); - /* create sysfs variables */ err = device_create_file(&pm->pk->hdev->dev, sysfs_device_attr_channel); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 12354055d474..2f19b15f47f2 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -42,6 +42,7 @@ #define DUALSHOCK4_CONTROLLER_BT BIT(6) #define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER_USB | BUZZ_CONTROLLER | DUALSHOCK4_CONTROLLER_USB) +#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER_USB | DUALSHOCK4_CONTROLLER_USB) #define MAX_LEDS 4 @@ -499,6 +500,7 @@ struct sony_sc { __u8 right; #endif + __u8 worker_initialized; __u8 led_state[MAX_LEDS]; __u8 led_count; }; @@ -993,22 +995,11 @@ static int sony_init_ff(struct hid_device *hdev) return input_ff_create_memless(input_dev, NULL, sony_play_effect); } -static void sony_destroy_ff(struct hid_device *hdev) -{ - struct sony_sc *sc = hid_get_drvdata(hdev); - - cancel_work_sync(&sc->state_worker); -} - #else static int sony_init_ff(struct hid_device *hdev) { return 0; } - -static void sony_destroy_ff(struct hid_device *hdev) -{ -} #endif static int sony_set_output_report(struct sony_sc *sc, int req_id, int req_size) @@ -1077,6 +1068,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) if (sc->quirks & SIXAXIS_CONTROLLER_USB) { hdev->hid_output_raw_report = sixaxis_usb_output_raw_report; ret = sixaxis_set_operational_usb(hdev); + + sc->worker_initialized = 1; INIT_WORK(&sc->state_worker, sixaxis_state_worker); } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) @@ -1087,6 +1080,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret < 0) goto err_stop; + sc->worker_initialized = 1; INIT_WORK(&sc->state_worker, dualshock4_state_worker); } else { ret = 0; @@ -1101,9 +1095,11 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_stop; } - ret = sony_init_ff(hdev); - if (ret < 0) - goto err_stop; + if (sc->quirks & SONY_FF_SUPPORT) { + ret = sony_init_ff(hdev); + if (ret < 0) + goto err_stop; + } return 0; err_stop: @@ -1120,7 +1116,8 @@ static void sony_remove(struct hid_device *hdev) if (sc->quirks & SONY_LED_SUPPORT) sony_leds_remove(hdev); - sony_destroy_ff(hdev); + if (sc->worker_initialized) + cancel_work_sync(&sc->state_worker); hid_hw_stop(hdev); } diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index cb0137b3718d..ab24ce2eb28f 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -320,13 +320,13 @@ static void drop_ref(struct hidraw *hidraw, int exists_bit) hid_hw_close(hidraw->hid); wake_up_interruptible(&hidraw->wait); } + device_destroy(hidraw_class, + MKDEV(hidraw_major, hidraw->minor)); } else { --hidraw->open; } if (!hidraw->open) { if (!hidraw->exist) { - device_destroy(hidraw_class, - MKDEV(hidraw_major, hidraw->minor)); hidraw_table[hidraw->minor] = NULL; kfree(hidraw); } else { diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index 0a74b5661186..5e4dfa4cfe22 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o hv_vmbus-y := vmbus_drv.o \ hv.o connection.o channel.o \ channel_mgmt.o ring_buffer.o -hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o +hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 69ea36f07b4d..602ca86a6488 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -27,6 +27,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/hyperv.h> +#include <linux/uio.h> #include "hyperv_vmbus.h" @@ -554,14 +555,14 @@ EXPORT_SYMBOL_GPL(vmbus_close); * * Mainly used by Hyper-V drivers. */ -int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, +int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, u32 bufferlen, u64 requestid, enum vmbus_packet_type type, u32 flags) { struct vmpacket_descriptor desc; u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen; u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64)); - struct scatterlist bufferlist[3]; + struct kvec bufferlist[3]; u64 aligned_data = 0; int ret; bool signal = false; @@ -575,11 +576,12 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, desc.len8 = (u16)(packetlen_aligned >> 3); desc.trans_id = requestid; - sg_init_table(bufferlist, 3); - sg_set_buf(&bufferlist[0], &desc, sizeof(struct vmpacket_descriptor)); - sg_set_buf(&bufferlist[1], buffer, bufferlen); - sg_set_buf(&bufferlist[2], &aligned_data, - packetlen_aligned - packetlen); + bufferlist[0].iov_base = &desc; + bufferlist[0].iov_len = sizeof(struct vmpacket_descriptor); + bufferlist[1].iov_base = buffer; + bufferlist[1].iov_len = bufferlen; + bufferlist[2].iov_base = &aligned_data; + bufferlist[2].iov_len = (packetlen_aligned - packetlen); ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); @@ -605,7 +607,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, u32 descsize; u32 packetlen; u32 packetlen_aligned; - struct scatterlist bufferlist[3]; + struct kvec bufferlist[3]; u64 aligned_data = 0; bool signal = false; @@ -637,11 +639,12 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, desc.range[i].pfn = pagebuffers[i].pfn; } - sg_init_table(bufferlist, 3); - sg_set_buf(&bufferlist[0], &desc, descsize); - sg_set_buf(&bufferlist[1], buffer, bufferlen); - sg_set_buf(&bufferlist[2], &aligned_data, - packetlen_aligned - packetlen); + bufferlist[0].iov_base = &desc; + bufferlist[0].iov_len = descsize; + bufferlist[1].iov_base = buffer; + bufferlist[1].iov_len = bufferlen; + bufferlist[2].iov_base = &aligned_data; + bufferlist[2].iov_len = (packetlen_aligned - packetlen); ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); @@ -665,7 +668,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, u32 descsize; u32 packetlen; u32 packetlen_aligned; - struct scatterlist bufferlist[3]; + struct kvec bufferlist[3]; u64 aligned_data = 0; bool signal = false; u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, @@ -700,11 +703,12 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, memcpy(desc.range.pfn_array, multi_pagebuffer->pfn_array, pfncount * sizeof(u64)); - sg_init_table(bufferlist, 3); - sg_set_buf(&bufferlist[0], &desc, descsize); - sg_set_buf(&bufferlist[1], buffer, bufferlen); - sg_set_buf(&bufferlist[2], &aligned_data, - packetlen_aligned - packetlen); + bufferlist[0].iov_base = &desc; + bufferlist[0].iov_len = descsize; + bufferlist[1].iov_base = buffer; + bufferlist[1].iov_len = bufferlen; + bufferlist[2].iov_base = &aligned_data; + bufferlist[2].iov_len = (packetlen_aligned - packetlen); ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 7e17a5495e02..7e6d78dc9437 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1171,7 +1171,8 @@ static int dm_thread_func(void *dm_dev) int t; while (!kthread_should_stop()) { - t = wait_for_completion_timeout(&dm_device.config_event, 1*HZ); + t = wait_for_completion_interruptible_timeout( + &dm_device.config_event, 1*HZ); /* * The host expects us to post information on the memory * pressure every second. diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c new file mode 100644 index 000000000000..eaaa3d843b80 --- /dev/null +++ b/drivers/hv/hv_fcopy.c @@ -0,0 +1,414 @@ +/* + * An implementation of file copy service. + * + * Copyright (C) 2014, Microsoft, Inc. + * + * Author : K. Y. Srinivasan <ksrinivasan@novell.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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/semaphore.h> +#include <linux/fs.h> +#include <linux/nls.h> +#include <linux/workqueue.h> +#include <linux/cdev.h> +#include <linux/hyperv.h> +#include <linux/sched.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> + +#include "hyperv_vmbus.h" + +#define WIN8_SRV_MAJOR 1 +#define WIN8_SRV_MINOR 1 +#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) + +/* + * Global state maintained for transaction that is being processed. + * For a class of integration services, including the "file copy service", + * the specified protocol is a "request/response" protocol which means that + * there can only be single outstanding transaction from the host at any + * given point in time. We use this to simplify memory management in this + * driver - we cache and process only one message at a time. + * + * While the request/response protocol is guaranteed by the host, we further + * ensure this by serializing packet processing in this driver - we do not + * read additional packets from the VMBUs until the current packet is fully + * handled. + * + * The transaction "active" state is set when we receive a request from the + * host and we cleanup this state when the transaction is completed - when we + * respond to the host with our response. When the transaction active state is + * set, we defer handling incoming packets. + */ + +static struct { + bool active; /* transaction status - active or not */ + int recv_len; /* number of bytes received. */ + struct hv_fcopy_hdr *fcopy_msg; /* current message */ + struct hv_start_fcopy message; /* sent to daemon */ + struct vmbus_channel *recv_channel; /* chn we got the request */ + u64 recv_req_id; /* request ID. */ + void *fcopy_context; /* for the channel callback */ + struct semaphore read_sema; +} fcopy_transaction; + +static bool opened; /* currently device opened */ + +/* + * Before we can accept copy messages from the host, we need + * to handshake with the user level daemon. This state tracks + * if we are in the handshake phase. + */ +static bool in_hand_shake = true; +static void fcopy_send_data(void); +static void fcopy_respond_to_host(int error); +static void fcopy_work_func(struct work_struct *dummy); +static DECLARE_DELAYED_WORK(fcopy_work, fcopy_work_func); +static u8 *recv_buffer; + +static void fcopy_work_func(struct work_struct *dummy) +{ + /* + * If the timer fires, the user-mode component has not responded; + * process the pending transaction. + */ + fcopy_respond_to_host(HV_E_FAIL); +} + +static int fcopy_handle_handshake(u32 version) +{ + switch (version) { + case FCOPY_CURRENT_VERSION: + break; + default: + /* + * For now we will fail the registration. + * If and when we have multiple versions to + * deal with, we will be backward compatible. + * We will add this code when needed. + */ + return -EINVAL; + } + pr_info("FCP: user-mode registering done. Daemon version: %d\n", + version); + fcopy_transaction.active = false; + if (fcopy_transaction.fcopy_context) + hv_fcopy_onchannelcallback(fcopy_transaction.fcopy_context); + in_hand_shake = false; + return 0; +} + +static void fcopy_send_data(void) +{ + struct hv_start_fcopy *smsg_out = &fcopy_transaction.message; + int operation = fcopy_transaction.fcopy_msg->operation; + struct hv_start_fcopy *smsg_in; + + /* + * The strings sent from the host are encoded in + * in utf16; convert it to utf8 strings. + * The host assures us that the utf16 strings will not exceed + * the max lengths specified. We will however, reserve room + * for the string terminating character - in the utf16s_utf8s() + * function we limit the size of the buffer where the converted + * string is placed to W_MAX_PATH -1 to guarantee + * that the strings can be properly terminated! + */ + + switch (operation) { + case START_FILE_COPY: + memset(smsg_out, 0, sizeof(struct hv_start_fcopy)); + smsg_out->hdr.operation = operation; + smsg_in = (struct hv_start_fcopy *)fcopy_transaction.fcopy_msg; + + utf16s_to_utf8s((wchar_t *)smsg_in->file_name, W_MAX_PATH, + UTF16_LITTLE_ENDIAN, + (__u8 *)smsg_out->file_name, W_MAX_PATH - 1); + + utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH, + UTF16_LITTLE_ENDIAN, + (__u8 *)smsg_out->path_name, W_MAX_PATH - 1); + + smsg_out->copy_flags = smsg_in->copy_flags; + smsg_out->file_size = smsg_in->file_size; + break; + + default: + break; + } + up(&fcopy_transaction.read_sema); + return; +} + +/* + * Send a response back to the host. + */ + +static void +fcopy_respond_to_host(int error) +{ + struct icmsg_hdr *icmsghdr; + u32 buf_len; + struct vmbus_channel *channel; + u64 req_id; + + /* + * Copy the global state for completing the transaction. Note that + * only one transaction can be active at a time. This is guaranteed + * by the file copy protocol implemented by the host. Furthermore, + * the "transaction active" state we maintain ensures that there can + * only be one active transaction at a time. + */ + + buf_len = fcopy_transaction.recv_len; + channel = fcopy_transaction.recv_channel; + req_id = fcopy_transaction.recv_req_id; + + fcopy_transaction.active = false; + + icmsghdr = (struct icmsg_hdr *) + &recv_buffer[sizeof(struct vmbuspipe_hdr)]; + + if (channel->onchannel_callback == NULL) + /* + * We have raced with util driver being unloaded; + * silently return. + */ + return; + + icmsghdr->status = error; + icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; + vmbus_sendpacket(channel, recv_buffer, buf_len, req_id, + VM_PKT_DATA_INBAND, 0); +} + +void hv_fcopy_onchannelcallback(void *context) +{ + struct vmbus_channel *channel = context; + u32 recvlen; + u64 requestid; + struct hv_fcopy_hdr *fcopy_msg; + struct icmsg_hdr *icmsghdr; + struct icmsg_negotiate *negop = NULL; + int util_fw_version; + int fcopy_srv_version; + + if (fcopy_transaction.active) { + /* + * We will defer processing this callback once + * the current transaction is complete. + */ + fcopy_transaction.fcopy_context = context; + return; + } + + vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, + &requestid); + if (recvlen <= 0) + return; + + icmsghdr = (struct icmsg_hdr *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr)]; + if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { + util_fw_version = UTIL_FW_VERSION; + fcopy_srv_version = WIN8_SRV_VERSION; + vmbus_prep_negotiate_resp(icmsghdr, negop, recv_buffer, + util_fw_version, fcopy_srv_version); + } else { + fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ + sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + /* + * Stash away this global state for completing the + * transaction; note transactions are serialized. + */ + + fcopy_transaction.active = true; + fcopy_transaction.recv_len = recvlen; + fcopy_transaction.recv_channel = channel; + fcopy_transaction.recv_req_id = requestid; + fcopy_transaction.fcopy_msg = fcopy_msg; + + /* + * Send the information to the user-level daemon. + */ + fcopy_send_data(); + schedule_delayed_work(&fcopy_work, 5*HZ); + return; + } + icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; + vmbus_sendpacket(channel, recv_buffer, recvlen, requestid, + VM_PKT_DATA_INBAND, 0); +} + +/* + * Create a char device that can support read/write for passing + * the payload. + */ + +static ssize_t fcopy_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + void *src; + size_t copy_size; + int operation; + + /* + * Wait until there is something to be read. + */ + if (down_interruptible(&fcopy_transaction.read_sema)) + return -EINTR; + + /* + * The channel may be rescinded and in this case, we will wakeup the + * the thread blocked on the semaphore and we will use the opened + * state to correctly handle this case. + */ + if (!opened) + return -ENODEV; + + operation = fcopy_transaction.fcopy_msg->operation; + + if (operation == START_FILE_COPY) { + src = &fcopy_transaction.message; + copy_size = sizeof(struct hv_start_fcopy); + if (count < copy_size) + return 0; + } else { + src = fcopy_transaction.fcopy_msg; + copy_size = sizeof(struct hv_do_fcopy); + if (count < copy_size) + return 0; + } + if (copy_to_user(buf, src, copy_size)) + return -EFAULT; + + return copy_size; +} + +static ssize_t fcopy_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int response = 0; + + if (count != sizeof(int)) + return -EINVAL; + + if (copy_from_user(&response, buf, sizeof(int))) + return -EFAULT; + + if (in_hand_shake) { + if (fcopy_handle_handshake(response)) + return -EINVAL; + return sizeof(int); + } + + /* + * Complete the transaction by forwarding the result + * to the host. But first, cancel the timeout. + */ + if (cancel_delayed_work_sync(&fcopy_work)) + fcopy_respond_to_host(response); + + return sizeof(int); +} + +static int fcopy_open(struct inode *inode, struct file *f) +{ + /* + * The user level daemon that will open this device is + * really an extension of this driver. We can have only + * active open at a time. + */ + if (opened) + return -EBUSY; + + /* + * The daemon is alive; setup the state. + */ + opened = true; + return 0; +} + +static int fcopy_release(struct inode *inode, struct file *f) +{ + /* + * The daemon has exited; reset the state. + */ + in_hand_shake = true; + opened = false; + return 0; +} + + +static const struct file_operations fcopy_fops = { + .read = fcopy_read, + .write = fcopy_write, + .release = fcopy_release, + .open = fcopy_open, +}; + +static struct miscdevice fcopy_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vmbus/hv_fcopy", + .fops = &fcopy_fops, +}; + +static int fcopy_dev_init(void) +{ + return misc_register(&fcopy_misc); +} + +static void fcopy_dev_deinit(void) +{ + + /* + * The device is going away - perhaps because the + * host has rescinded the channel. Setup state so that + * user level daemon can gracefully exit if it is blocked + * on the read semaphore. + */ + opened = false; + /* + * Signal the semaphore as the device is + * going away. + */ + up(&fcopy_transaction.read_sema); + misc_deregister(&fcopy_misc); +} + +int hv_fcopy_init(struct hv_util_service *srv) +{ + recv_buffer = srv->recv_buffer; + + /* + * When this driver loads, the user level daemon that + * processes the host requests may not yet be running. + * Defer processing channel callbacks until the daemon + * has registered. + */ + fcopy_transaction.active = true; + sema_init(&fcopy_transaction.read_sema, 0); + + return fcopy_dev_init(); +} + +void hv_fcopy_deinit(void) +{ + cancel_delayed_work_sync(&fcopy_work); + fcopy_dev_deinit(); +} diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 09988b289622..ea852537307e 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -113,7 +113,7 @@ kvp_register(int reg_value) kvp_msg->kvp_hdr.operation = reg_value; strcpy(version, HV_DRV_VERSION); msg->len = sizeof(struct hv_kvp_msg); - cn_netlink_send(msg, 0, GFP_ATOMIC); + cn_netlink_send(msg, 0, 0, GFP_ATOMIC); kfree(msg); } } @@ -435,7 +435,7 @@ kvp_send_key(struct work_struct *dummy) } msg->len = sizeof(struct hv_kvp_msg); - cn_netlink_send(msg, 0, GFP_ATOMIC); + cn_netlink_send(msg, 0, 0, GFP_ATOMIC); kfree(msg); return; diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index 0c3546224376..34f14fddb666 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -98,7 +98,7 @@ static void vss_send_op(struct work_struct *dummy) vss_msg->vss_hdr.operation = op; msg->len = sizeof(struct hv_vss_msg); - cn_netlink_send(msg, 0, GFP_ATOMIC); + cn_netlink_send(msg, 0, 0, GFP_ATOMIC); kfree(msg); return; diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 62dfd246b948..dd761806f0e8 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -28,6 +28,7 @@ #include <linux/reboot.h> #include <linux/hyperv.h> +#include "hyperv_vmbus.h" #define SD_MAJOR 3 #define SD_MINOR 0 @@ -82,6 +83,12 @@ static struct hv_util_service util_vss = { .util_deinit = hv_vss_deinit, }; +static struct hv_util_service util_fcopy = { + .util_cb = hv_fcopy_onchannelcallback, + .util_init = hv_fcopy_init, + .util_deinit = hv_fcopy_deinit, +}; + static void perform_shutdown(struct work_struct *dummy) { orderly_poweroff(true); @@ -401,6 +408,10 @@ static const struct hv_vmbus_device_id id_table[] = { { HV_VSS_GUID, .driver_data = (unsigned long)&util_vss }, + /* File copy GUID */ + { HV_FCOPY_GUID, + .driver_data = (unsigned long)&util_fcopy + }, { }, }; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index e05517616a06..860134da8039 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -559,8 +559,8 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer, void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info); int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info, - struct scatterlist *sglist, - u32 sgcount, bool *signal); + struct kvec *kv_list, + u32 kv_count, bool *signal); int hv_ringbuffer_peek(struct hv_ring_buffer_info *ring_info, void *buffer, u32 buflen); @@ -669,5 +669,9 @@ int vmbus_set_event(struct vmbus_channel *channel); void vmbus_on_event(unsigned long data); +int hv_fcopy_init(struct hv_util_service *); +void hv_fcopy_deinit(void); +void hv_fcopy_onchannelcallback(void *); + #endif /* _HYPERV_VMBUS_H */ diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 26c93cf9f6be..15db66b74141 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -26,6 +26,7 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/hyperv.h> +#include <linux/uio.h> #include "hyperv_vmbus.h" @@ -387,23 +388,20 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) * */ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, - struct scatterlist *sglist, u32 sgcount, bool *signal) + struct kvec *kv_list, u32 kv_count, bool *signal) { int i = 0; u32 bytes_avail_towrite; u32 bytes_avail_toread; u32 totalbytes_towrite = 0; - struct scatterlist *sg; u32 next_write_location; u32 old_write; u64 prev_indices = 0; unsigned long flags; - for_each_sg(sglist, sg, sgcount, i) - { - totalbytes_towrite += sg->length; - } + for (i = 0; i < kv_count; i++) + totalbytes_towrite += kv_list[i].iov_len; totalbytes_towrite += sizeof(u64); @@ -427,12 +425,11 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info, old_write = next_write_location; - for_each_sg(sglist, sg, sgcount, i) - { + for (i = 0; i < kv_count; i++) { next_write_location = hv_copyto_ringbuffer(outring_info, next_write_location, - sg_virt(sg), - sg->length); + kv_list[i].iov_base, + kv_list[i].iov_len); } /* Set previous packet start */ diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 077bb1bdac34..8e53a3c2607e 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -25,7 +25,6 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> -#include <linux/irq.h> #include <linux/interrupt.h> #include <linux/sysctl.h> #include <linux/slab.h> @@ -44,6 +43,12 @@ static struct tasklet_struct msg_dpc; static struct completion probe_event; static int irq; +struct resource hyperv_mmio = { + .name = "hyperv mmio", + .flags = IORESOURCE_MEM, +}; +EXPORT_SYMBOL_GPL(hyperv_mmio); + static int vmbus_exists(void) { if (hv_acpi_dev == NULL) @@ -558,9 +563,6 @@ static struct bus_type hv_bus = { .dev_groups = vmbus_groups, }; -static const char *driver_name = "hyperv"; - - struct onmessage_work_context { struct work_struct work; struct hv_message msg; @@ -619,7 +621,7 @@ static void vmbus_on_msg_dpc(unsigned long data) } } -static irqreturn_t vmbus_isr(int irq, void *dev_id) +static void vmbus_isr(void) { int cpu = smp_processor_id(); void *page_addr; @@ -629,7 +631,7 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) page_addr = hv_context.synic_event_page[cpu]; if (page_addr == NULL) - return IRQ_NONE; + return; event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; @@ -665,28 +667,8 @@ static irqreturn_t vmbus_isr(int irq, void *dev_id) msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; /* Check if there are actual msgs to be processed */ - if (msg->header.message_type != HVMSG_NONE) { - handled = true; + if (msg->header.message_type != HVMSG_NONE) tasklet_schedule(&msg_dpc); - } - - if (handled) - return IRQ_HANDLED; - else - return IRQ_NONE; -} - -/* - * vmbus interrupt flow handler: - * vmbus interrupts can concurrently occur on multiple CPUs and - * can be handled concurrently. - */ - -static void vmbus_flow_handler(unsigned int irq, struct irq_desc *desc) -{ - kstat_incr_irqs_this_cpu(irq, desc); - - desc->action->handler(irq, desc->action->dev_id); } /* @@ -715,25 +697,7 @@ static int vmbus_bus_init(int irq) if (ret) goto err_cleanup; - ret = request_irq(irq, vmbus_isr, 0, driver_name, hv_acpi_dev); - - if (ret != 0) { - pr_err("Unable to request IRQ %d\n", - irq); - goto err_unregister; - } - - /* - * Vmbus interrupts can be handled concurrently on - * different CPUs. Establish an appropriate interrupt flow - * handler that can support this model. - */ - irq_set_handler(irq, vmbus_flow_handler); - - /* - * Register our interrupt handler. - */ - hv_register_vmbus_handler(irq, vmbus_isr); + hv_setup_vmbus_irq(vmbus_isr); ret = hv_synic_alloc(); if (ret) @@ -753,9 +717,8 @@ static int vmbus_bus_init(int irq) err_alloc: hv_synic_free(); - free_irq(irq, hv_acpi_dev); + hv_remove_vmbus_irq(); -err_unregister: bus_unregister(&hv_bus); err_cleanup: @@ -886,18 +849,21 @@ void vmbus_device_unregister(struct hv_device *device_obj) /* - * VMBUS is an acpi enumerated device. Get the the IRQ information - * from DSDT. + * VMBUS is an acpi enumerated device. Get the the information we + * need from DSDT. */ -static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq) +static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) { + switch (res->type) { + case ACPI_RESOURCE_TYPE_IRQ: + irq = res->data.irq.interrupts[0]; + break; - if (res->type == ACPI_RESOURCE_TYPE_IRQ) { - struct acpi_resource_irq *irqp; - irqp = &res->data.irq; - - *((unsigned int *)irq) = irqp->interrupts[0]; + case ACPI_RESOURCE_TYPE_ADDRESS64: + hyperv_mmio.start = res->data.address64.minimum; + hyperv_mmio.end = res->data.address64.maximum; + break; } return AE_OK; @@ -906,18 +872,34 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *irq) static int vmbus_acpi_add(struct acpi_device *device) { acpi_status result; + int ret_val = -ENODEV; hv_acpi_dev = device; result = acpi_walk_resources(device->handle, METHOD_NAME__CRS, - vmbus_walk_resources, &irq); + vmbus_walk_resources, NULL); - if (ACPI_FAILURE(result)) { - complete(&probe_event); - return -ENODEV; + if (ACPI_FAILURE(result)) + goto acpi_walk_err; + /* + * The parent of the vmbus acpi device (Gen2 firmware) is the VMOD that + * has the mmio ranges. Get that. + */ + if (device->parent) { + result = acpi_walk_resources(device->parent->handle, + METHOD_NAME__CRS, + vmbus_walk_resources, NULL); + + if (ACPI_FAILURE(result)) + goto acpi_walk_err; + if (hyperv_mmio.start && hyperv_mmio.end) + request_resource(&iomem_resource, &hyperv_mmio); } + ret_val = 0; + +acpi_walk_err: complete(&probe_event); - return 0; + return ret_val; } static const struct acpi_device_id vmbus_acpi_device_ids[] = { @@ -947,7 +929,6 @@ static int __init hv_acpi_init(void) /* * Get irq resources first. */ - ret = acpi_bus_register_driver(&vmbus_acpi_driver); if (ret) @@ -978,8 +959,7 @@ cleanup: static void __exit vmbus_exit(void) { - - free_irq(irq, hv_acpi_dev); + hv_remove_vmbus_irq(); vmbus_free_channels(); bus_unregister(&hv_bus); hv_cleanup(); diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5ce43d8dfa98..f288b60a87be 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -111,22 +111,6 @@ config SENSORS_AD7418 This driver can also be built as a module. If so, the module will be called ad7418. -config SENSORS_ADCXX - tristate "National Semiconductor ADCxxxSxxx" - depends on SPI_MASTER - help - If you say yes here you get support for the National Semiconductor - ADC<bb><c>S<sss> chip family, where - * bb is the resolution in number of bits (8, 10, 12) - * c is the number of channels (1, 2, 4, 8) - * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500 - kSPS and 101 for 1 MSPS) - - Examples : ADC081S101, ADC124S501, ... - - This driver can also be built as a module. If so, the module - will be called adcxx. - config SENSORS_ADM1021 tristate "Analog Devices ADM1021 and compatibles" depends on I2C @@ -312,6 +296,31 @@ config SENSORS_FAM15H_POWER This driver can also be built as a module. If so, the module will be called fam15h_power. +config SENSORS_APPLESMC + tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" + depends on INPUT && X86 + select NEW_LEDS + select LEDS_CLASS + select INPUT_POLLDEV + default n + help + This driver provides support for the Apple System Management + Controller, which provides an accelerometer (Apple Sudden Motion + Sensor), light sensors, temperature sensors, keyboard backlight + control and fan control. + + Only Intel-based Apple's computers are supported (MacBook Pro, + MacBook, MacMini). + + Data from the different sensors, keyboard backlight control and fan + control are accessible via sysfs. + + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. + + Say Y here if you have an applicable laptop and want to experience + the awesome power of applesmc. + config SENSORS_ASB100 tristate "Asus ASB100 Bach" depends on X86 && I2C @@ -435,6 +444,12 @@ config SENSORS_F75375S This driver can also be built as a module. If so, the module will be called f75375s. +config SENSORS_MC13783_ADC + tristate "Freescale MC13783/MC13892 ADC" + depends on MFD_MC13XXX + help + Support for the A/D converter on MC13783 and MC13892 PMIC. + config SENSORS_FSCHMD tristate "Fujitsu Siemens Computers sensor chips" depends on X86 && I2C @@ -451,26 +466,6 @@ config SENSORS_FSCHMD This driver can also be built as a module. If so, the module will be called fschmd. -config SENSORS_G760A - tristate "GMT G760A" - depends on I2C - help - If you say yes here you get support for Global Mixed-mode - Technology Inc G760A fan speed PWM controller chips. - - This driver can also be built as a module. If so, the module - will be called g760a. - -config SENSORS_G762 - tristate "GMT G762 and G763" - depends on I2C - help - If you say yes here you get support for Global Mixed-mode - Technology Inc G762 and G763 fan speed PWM controller chips. - - This driver can also be built as a module. If so, the module - will be called g762. - config SENSORS_GL518SM tristate "Genesys Logic GL518SM" depends on I2C @@ -492,6 +487,26 @@ config SENSORS_GL520SM This driver can also be built as a module. If so, the module will be called gl520sm. +config SENSORS_G760A + tristate "GMT G760A" + depends on I2C + help + If you say yes here you get support for Global Mixed-mode + Technology Inc G760A fan speed PWM controller chips. + + This driver can also be built as a module. If so, the module + will be called g760a. + +config SENSORS_G762 + tristate "GMT G762 and G763" + depends on I2C + help + If you say yes here you get support for Global Mixed-mode + Technology Inc G762 and G763 fan speed PWM controller chips. + + This driver can also be built as a module. If so, the module + will be called g762. + config SENSORS_GPIO_FAN tristate "GPIO fan" depends on GPIOLIB @@ -511,24 +526,6 @@ config SENSORS_HIH6130 This driver can also be built as a module. If so, the module will be called hih6130. -config SENSORS_HTU21 - tristate "Measurement Specialties HTU21D humidity/temperature sensors" - depends on I2C - help - If you say yes here you get support for the Measurement Specialties - HTU21D humidity and temperature sensors. - - This driver can also be built as a module. If so, the module - will be called htu21. - -config SENSORS_CORETEMP - tristate "Intel Core/Core2/Atom temperature sensor" - depends on X86 - help - If you say yes here you get support for the temperature - sensor inside your CPU. Most of the family 6 CPUs - are supported. Check Documentation/hwmon/coretemp for details. - config SENSORS_IBMAEM tristate "IBM Active Energy Manager temperature/power sensors and control" select IPMI_SI @@ -566,6 +563,14 @@ 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_CORETEMP + tristate "Intel Core/Core2/Atom temperature sensor" + depends on X86 + help + If you say yes here you get support for the temperature + sensor inside your CPU. Most of the family 6 CPUs + are supported. Check Documentation/hwmon/coretemp for details. + config SENSORS_IT87 tristate "ITE IT87xx and compatibles" depends on !PPC @@ -614,6 +619,219 @@ config SENSORS_LINEAGE This driver can also be built as a module. If so, the module will be called lineage-pem. +config SENSORS_LTC2945 + tristate "Linear Technology LTC2945" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC2945 + I2C System Monitor. + + This driver can also be built as a module. If so, the module will + be called ltc2945. + +config SENSORS_LTC4151 + tristate "Linear Technology LTC4151" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4151 + High Voltage I2C Current and Voltage Monitor interface. + + This driver can also be built as a module. If so, the module will + be called ltc4151. + +config SENSORS_LTC4215 + tristate "Linear Technology LTC4215" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4215 + Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4215. + +config SENSORS_LTC4222 + tristate "Linear Technology LTC4222" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC4222 + Dual Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4222. + +config SENSORS_LTC4245 + tristate "Linear Technology LTC4245" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4245 + Multiple Supply Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4245. + +config SENSORS_LTC4260 + tristate "Linear Technology LTC4260" + depends on I2C + select REGMAP_I2C + default n + help + If you say yes here you get support for Linear Technology LTC4260 + Positive Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4260. + +config SENSORS_LTC4261 + tristate "Linear Technology LTC4261" + depends on I2C + default n + help + If you say yes here you get support for Linear Technology LTC4261 + Negative Voltage Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4261. + +config SENSORS_MAX1111 + tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles" + depends on SPI_MASTER + help + Say y here to support Maxim's MAX1110, MAX1111, MAX1112, and MAX1113 + ADC chips. + + This driver can also be built as a module. If so, the module + will be called max1111. + +config SENSORS_MAX16065 + tristate "Maxim MAX16065 System Manager and compatibles" + depends on I2C + help + If you say yes here you get support for hardware monitoring + capabilities of the following Maxim System Manager chips. + MAX16065 + MAX16066 + MAX16067 + MAX16068 + MAX16070 + MAX16071 + + This driver can also be built as a module. If so, the module + will be called max16065. + +config SENSORS_MAX1619 + tristate "Maxim MAX1619 sensor chip" + depends on I2C + help + If you say yes here you get support for MAX1619 sensor chip. + + This driver can also be built as a module. If so, the module + will be called max1619. + +config SENSORS_MAX1668 + tristate "Maxim MAX1668 and compatibles" + depends on I2C + help + If you say yes here you get support for MAX1668, MAX1989 and + MAX1805 chips. + + This driver can also be built as a module. If so, the module + will be called max1668. + +config SENSORS_MAX197 + tristate "Maxim MAX197 and compatibles" + help + Support for the Maxim MAX197 A/D converter. + Support will include, but not be limited to, MAX197, and MAX199. + + This driver can also be built as a module. If so, the module + will be called max197. + +config SENSORS_MAX6639 + tristate "Maxim MAX6639 sensor chip" + depends on I2C + help + If you say yes here you get support for the MAX6639 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6639. + +config SENSORS_MAX6642 + tristate "Maxim MAX6642 sensor chip" + depends on I2C + help + If you say yes here you get support for MAX6642 sensor chip. + MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor + with Overtemperature Alarm from Maxim. + + This driver can also be built as a module. If so, the module + will be called max6642. + +config SENSORS_MAX6650 + tristate "Maxim MAX6650 sensor chip" + depends on I2C + help + If you say yes here you get support for the MAX6650 / MAX6651 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6650. + +config SENSORS_MAX6697 + tristate "Maxim MAX6697 and compatibles" + depends on I2C + help + If you say yes here you get support for MAX6581, MAX6602, MAX6622, + MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699 + temperature sensor chips. + + This driver can also be built as a module. If so, the module + will be called max6697. + +config SENSORS_HTU21 + tristate "Measurement Specialties HTU21D humidity/temperature sensors" + depends on I2C + help + If you say yes here you get support for the Measurement Specialties + HTU21D humidity and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called htu21. + +config SENSORS_MCP3021 + tristate "Microchip MCP3021 and compatibles" + depends on I2C + help + If you say yes here you get support for MCP3021 and MCP3221. + The MCP3021 is a A/D converter (ADC) with 10-bit and the MCP3221 + with 12-bit resolution. + + This driver can also be built as a module. If so, the module + will be called mcp3021. + +config SENSORS_ADCXX + tristate "National Semiconductor ADCxxxSxxx" + depends on SPI_MASTER + help + If you say yes here you get support for the National Semiconductor + ADC<bb><c>S<sss> chip family, where + * bb is the resolution in number of bits (8, 10, 12) + * c is the number of channels (1, 2, 4, 8) + * sss is the maximum conversion speed (021 for 200 kSPS, 051 for 500 + kSPS and 101 for 1 MSPS) + + Examples : ADC081S101, ADC124S501, ... + + This driver can also be built as a module. If so, the module + will be called adcxx. + config SENSORS_LM63 tristate "National Semiconductor LM63 and compatibles" depends on I2C @@ -776,50 +994,6 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. -config SENSORS_LTC4151 - tristate "Linear Technology LTC4151" - depends on I2C - default n - help - If you say yes here you get support for Linear Technology LTC4151 - High Voltage I2C Current and Voltage Monitor interface. - - This driver can also be built as a module. If so, the module will - be called ltc4151. - -config SENSORS_LTC4215 - tristate "Linear Technology LTC4215" - depends on I2C - default n - help - If you say yes here you get support for Linear Technology LTC4215 - Hot Swap Controller I2C interface. - - This driver can also be built as a module. If so, the module will - be called ltc4215. - -config SENSORS_LTC4245 - tristate "Linear Technology LTC4245" - depends on I2C - default n - help - If you say yes here you get support for Linear Technology LTC4245 - Multiple Supply Hot Swap Controller I2C interface. - - This driver can also be built as a module. If so, the module will - be called ltc4245. - -config SENSORS_LTC4261 - tristate "Linear Technology LTC4261" - depends on I2C - default n - help - If you say yes here you get support for Linear Technology LTC4261 - Negative Voltage Hot Swap Controller I2C interface. - - This driver can also be built as a module. If so, the module will - be called ltc4261. - config SENSORS_LM95234 tristate "National Semiconductor LM95234" depends on I2C @@ -849,125 +1023,33 @@ config SENSORS_LM95245 This driver can also be built as a module. If so, the module will be called lm95245. -config SENSORS_MAX1111 - tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles" - depends on SPI_MASTER - help - Say y here to support Maxim's MAX1110, MAX1111, MAX1112, and MAX1113 - ADC chips. - - This driver can also be built as a module. If so, the module - will be called max1111. - -config SENSORS_MAX16065 - tristate "Maxim MAX16065 System Manager and compatibles" - depends on I2C - help - If you say yes here you get support for hardware monitoring - capabilities of the following Maxim System Manager chips. - MAX16065 - MAX16066 - MAX16067 - MAX16068 - MAX16070 - MAX16071 - - This driver can also be built as a module. If so, the module - will be called max16065. - -config SENSORS_MAX1619 - tristate "Maxim MAX1619 sensor chip" - depends on I2C - help - If you say yes here you get support for MAX1619 sensor chip. - - This driver can also be built as a module. If so, the module - will be called max1619. - -config SENSORS_MAX1668 - tristate "Maxim MAX1668 and compatibles" - depends on I2C - help - If you say yes here you get support for MAX1668, MAX1989 and - MAX1805 chips. - - This driver can also be built as a module. If so, the module - will be called max1668. - -config SENSORS_MAX197 - tristate "Maxim MAX197 and compatibles" - help - Support for the Maxim MAX197 A/D converter. - Support will include, but not be limited to, MAX197, and MAX199. - - This driver can also be built as a module. If so, the module - will be called max197. - -config SENSORS_MAX6639 - tristate "Maxim MAX6639 sensor chip" - depends on I2C - help - If you say yes here you get support for the MAX6639 - sensor chips. - - This driver can also be built as a module. If so, the module - will be called max6639. - -config SENSORS_MAX6642 - tristate "Maxim MAX6642 sensor chip" - depends on I2C - help - If you say yes here you get support for MAX6642 sensor chip. - MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor - with Overtemperature Alarm from Maxim. - - This driver can also be built as a module. If so, the module - will be called max6642. - -config SENSORS_MAX6650 - tristate "Maxim MAX6650 sensor chip" - depends on I2C - help - If you say yes here you get support for the MAX6650 / MAX6651 - sensor chips. - - This driver can also be built as a module. If so, the module - will be called max6650. - -config SENSORS_MAX6697 - tristate "Maxim MAX6697 and compatibles" - depends on I2C - help - If you say yes here you get support for MAX6581, MAX6602, MAX6622, - MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699 - temperature sensor chips. - - This driver can also be built as a module. If so, the module - will be called max6697. - -config SENSORS_MCP3021 - tristate "Microchip MCP3021 and compatibles" - depends on I2C +config SENSORS_PC87360 + tristate "National Semiconductor PC87360 family" + depends on !PPC + select HWMON_VID help - If you say yes here you get support for MCP3021 and MCP3221. - The MCP3021 is a A/D converter (ADC) with 10-bit and the MCP3221 - with 12-bit resolution. + If you say yes here you get access to the hardware monitoring + functions of the National Semiconductor PC8736x Super-I/O chips. + The PC87360, PC87363 and PC87364 only have fan monitoring and + control. The PC87365 and PC87366 additionally have voltage and + temperature monitoring. This driver can also be built as a module. If so, the module - will be called mcp3021. + will be called pc87360. -config SENSORS_NCT6775 - tristate "Nuvoton NCT6775F and compatibles" +config SENSORS_PC87427 + tristate "National Semiconductor PC87427" depends on !PPC - select HWMON_VID help - If you say yes here you get support for the hardware monitoring - functionality of the Nuvoton NCT6775F, NCT6776F, NCT6779D - and compatible Super-I/O chips. This driver replaces the - w83627ehf driver for NCT6775F and NCT6776F. + If you say yes here you get access to the hardware monitoring + functions of the National Semiconductor PC87427 Super-I/O chip. + The chip has two distinct logical devices, one for fan speed + monitoring and control, and one for voltage and temperature + monitoring. Fan speed monitoring and control are supported, as + well as temperature monitoring. Voltages aren't supported yet. This driver can also be built as a module. If so, the module - will be called nct6775. + will be called pc87427. config SENSORS_NTC_THERMISTOR tristate "NTC thermistor support" @@ -983,33 +1065,18 @@ config SENSORS_NTC_THERMISTOR This driver can also be built as a module. If so, the module will be called ntc-thermistor. -config SENSORS_PC87360 - tristate "National Semiconductor PC87360 family" +config SENSORS_NCT6775 + tristate "Nuvoton NCT6775F and compatibles" depends on !PPC select HWMON_VID help - If you say yes here you get access to the hardware monitoring - functions of the National Semiconductor PC8736x Super-I/O chips. - The PC87360, PC87363 and PC87364 only have fan monitoring and - control. The PC87365 and PC87366 additionally have voltage and - temperature monitoring. - - This driver can also be built as a module. If so, the module - will be called pc87360. - -config SENSORS_PC87427 - tristate "National Semiconductor PC87427" - depends on !PPC - help - If you say yes here you get access to the hardware monitoring - functions of the National Semiconductor PC87427 Super-I/O chip. - The chip has two distinct logical devices, one for fan speed - monitoring and control, and one for voltage and temperature - monitoring. Fan speed monitoring and control are supported, as - well as temperature monitoring. Voltages aren't supported yet. + If you say yes here you get support for the hardware monitoring + functionality of the Nuvoton NCT6775F, NCT6776F, NCT6779D + and compatible Super-I/O chips. This driver replaces the + w83627ehf driver for NCT6775F and NCT6776F. This driver can also be built as a module. If so, the module - will be called pc87427. + will be called nct6775. config SENSORS_PCF8591 tristate "Philips PCF8591 ADC/DAC" @@ -1074,21 +1141,6 @@ config SENSORS_SIS5595 This driver can also be built as a module. If so, the module will be called sis5595. -config SENSORS_SMM665 - tristate "Summit Microelectronics SMM665" - depends on I2C - default n - help - If you say yes here you get support for the hardware monitoring - features of the Summit Microelectronics SMM665/SMM665B Six-Channel - Active DC Output Controller / Monitor. - - Other supported chips are SMM465, SMM665C, SMM764, and SMM766. - Support for those chips is untested. - - This driver can also be built as a module. If so, the module will - be called smm665. - config SENSORS_DME1737 tristate "SMSC DME1737, SCH311x and compatibles" depends on I2C && !PPC @@ -1210,6 +1262,31 @@ config SENSORS_SCH5636 This driver can also be built as a module. If so, the module will be called sch5636. +config SENSORS_SMM665 + tristate "Summit Microelectronics SMM665" + depends on I2C + default n + help + If you say yes here you get support for the hardware monitoring + features of the Summit Microelectronics SMM665/SMM665B Six-Channel + Active DC Output Controller / Monitor. + + Other supported chips are SMM465, SMM665C, SMM764, and SMM766. + Support for those chips is untested. + + This driver can also be built as a module. If so, the module will + be called smm665. + +config SENSORS_ADC128D818 + tristate "Texas Instruments ADC128D818" + depends on I2C + help + If you say yes here you get support for the Texas Instruments + ADC128D818 System Monitor with Temperature Sensor chip. + + This driver can also be built as a module. If so, the module + will be called adc128d818. + config SENSORS_ADS1015 tristate "Texas Instruments ADS1015" depends on I2C @@ -1525,37 +1602,6 @@ config SENSORS_ULTRA45 This driver provides support for the Ultra45 workstation environmental sensors. -config SENSORS_APPLESMC - tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" - depends on INPUT && X86 - select NEW_LEDS - select LEDS_CLASS - select INPUT_POLLDEV - default n - help - This driver provides support for the Apple System Management - Controller, which provides an accelerometer (Apple Sudden Motion - Sensor), light sensors, temperature sensors, keyboard backlight - control and fan control. - - Only Intel-based Apple's computers are supported (MacBook Pro, - MacBook, MacMini). - - Data from the different sensors, keyboard backlight control and fan - control are accessible via sysfs. - - This driver also provides an absolute input class device, allowing - the laptop to act as a pinball machine-esque joystick. - - Say Y here if you have an applicable laptop and want to experience - the awesome power of applesmc. - -config SENSORS_MC13783_ADC - tristate "Freescale MC13783/MC13892 ADC" - depends on MFD_MC13XXX - help - Support for the A/D converter on MC13783 and MC13892 PMIC. - if ACPI comment "ACPI drivers" diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index ec7cde06eb52..c48f9873ac73 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o obj-$(CONFIG_SENSORS_AD7314) += ad7314.o obj-$(CONFIG_SENSORS_AD7414) += ad7414.o obj-$(CONFIG_SENSORS_AD7418) += ad7418.o +obj-$(CONFIG_SENSORS_ADC128D818) += adc128d818.o obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o @@ -95,9 +96,12 @@ obj-$(CONFIG_SENSORS_LM93) += lm93.o obj-$(CONFIG_SENSORS_LM95234) += lm95234.o obj-$(CONFIG_SENSORS_LM95241) += lm95241.o obj-$(CONFIG_SENSORS_LM95245) += lm95245.o +obj-$(CONFIG_SENSORS_LTC2945) += ltc2945.o obj-$(CONFIG_SENSORS_LTC4151) += ltc4151.o obj-$(CONFIG_SENSORS_LTC4215) += ltc4215.o +obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o +obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX16065) += max16065.o diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c new file mode 100644 index 000000000000..5ffd81f19d01 --- /dev/null +++ b/drivers/hwmon/adc128d818.c @@ -0,0 +1,491 @@ +/* + * Driver for TI ADC128D818 System Monitor with Temperature Sensor + * + * Copyright (c) 2014 Guenter Roeck + * + * Derived from lm80.c + * Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> + * and Philip Edelbrock <phil@netroedge.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/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/regulator/consumer.h> +#include <linux/mutex.h> + +/* Addresses to scan + * The chip also supports addresses 0x35..0x37. Don't scan those addresses + * since they are also used by some EEPROMs, which may result in false + * positives. + */ +static const unsigned short normal_i2c[] = { + 0x1d, 0x1e, 0x1f, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; + +/* registers */ +#define ADC128_REG_IN_MAX(nr) (0x2a + (nr) * 2) +#define ADC128_REG_IN_MIN(nr) (0x2b + (nr) * 2) +#define ADC128_REG_IN(nr) (0x20 + (nr)) + +#define ADC128_REG_TEMP 0x27 +#define ADC128_REG_TEMP_MAX 0x38 +#define ADC128_REG_TEMP_HYST 0x39 + +#define ADC128_REG_CONFIG 0x00 +#define ADC128_REG_ALARM 0x01 +#define ADC128_REG_MASK 0x03 +#define ADC128_REG_CONV_RATE 0x07 +#define ADC128_REG_ONESHOT 0x09 +#define ADC128_REG_SHUTDOWN 0x0a +#define ADC128_REG_CONFIG_ADV 0x0b +#define ADC128_REG_BUSY_STATUS 0x0c + +#define ADC128_REG_MAN_ID 0x3e +#define ADC128_REG_DEV_ID 0x3f + +struct adc128_data { + struct i2c_client *client; + struct regulator *regulator; + int vref; /* Reference voltage in mV */ + struct mutex update_lock; + bool valid; /* true if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u16 in[3][7]; /* Register value, normalized to 12 bit + * 0: input voltage + * 1: min limit + * 2: max limit + */ + s16 temp[3]; /* Register value, normalized to 9 bit + * 0: sensor 1: limit 2: hyst + */ + u8 alarms; /* alarm register value */ +}; + +static struct adc128_data *adc128_update_device(struct device *dev) +{ + struct adc128_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct adc128_data *ret = data; + int i, rv; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + for (i = 0; i < 7; i++) { + rv = i2c_smbus_read_word_swapped(client, + ADC128_REG_IN(i)); + if (rv < 0) + goto abort; + data->in[0][i] = rv >> 4; + + rv = i2c_smbus_read_byte_data(client, + ADC128_REG_IN_MIN(i)); + if (rv < 0) + goto abort; + data->in[1][i] = rv << 4; + + rv = i2c_smbus_read_byte_data(client, + ADC128_REG_IN_MAX(i)); + if (rv < 0) + goto abort; + data->in[2][i] = rv << 4; + } + + rv = i2c_smbus_read_word_swapped(client, ADC128_REG_TEMP); + if (rv < 0) + goto abort; + data->temp[0] = rv >> 7; + + rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_MAX); + if (rv < 0) + goto abort; + data->temp[1] = rv << 1; + + rv = i2c_smbus_read_byte_data(client, ADC128_REG_TEMP_HYST); + if (rv < 0) + goto abort; + data->temp[2] = rv << 1; + + rv = i2c_smbus_read_byte_data(client, ADC128_REG_ALARM); + if (rv < 0) + goto abort; + data->alarms |= rv; + + data->last_updated = jiffies; + data->valid = true; + } + goto done; + +abort: + ret = ERR_PTR(rv); + data->valid = false; +done: + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t adc128_show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adc128_data *data = adc128_update_device(dev); + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = DIV_ROUND_CLOSEST(data->in[index][nr] * data->vref, 4095); + return sprintf(buf, "%d\n", val); +} + +static ssize_t adc128_set_in(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adc128_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr_2(attr)->index; + int nr = to_sensor_dev_attr_2(attr)->nr; + u8 reg, regval; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + /* 10 mV LSB on limit registers */ + regval = clamp_val(DIV_ROUND_CLOSEST(val, 10), 0, 255); + data->in[index][nr] = regval << 4; + reg = index == 1 ? ADC128_REG_IN_MIN(nr) : ADC128_REG_IN_MAX(nr); + i2c_smbus_write_byte_data(data->client, reg, regval); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t adc128_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adc128_data *data = adc128_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + int temp; + + if (IS_ERR(data)) + return PTR_ERR(data); + + temp = (data->temp[index] << 7) >> 7; /* sign extend */ + return sprintf(buf, "%d\n", temp * 500);/* 0.5 degrees C resolution */ +} + +static ssize_t adc128_set_temp(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adc128_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + long val; + int err; + s8 regval; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + mutex_lock(&data->update_lock); + regval = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + data->temp[index] = regval << 1; + i2c_smbus_write_byte_data(data->client, + index == 1 ? ADC128_REG_TEMP_MAX + : ADC128_REG_TEMP_HYST, + regval); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t adc128_show_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adc128_data *data = adc128_update_device(dev); + int mask = 1 << to_sensor_dev_attr(attr)->index; + u8 alarms; + + if (IS_ERR(data)) + return PTR_ERR(data); + + /* + * Clear an alarm after reporting it to user space. If it is still + * active, the next update sequence will set the alarm bit again. + */ + alarms = data->alarms; + data->alarms &= ~mask; + + return sprintf(buf, "%u\n", !!(alarms & mask)); +} + +static SENSOR_DEVICE_ATTR_2(in0_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 0, 0); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 0, 1); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 0, 2); + +static SENSOR_DEVICE_ATTR_2(in1_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 1, 0); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 1, 1); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 1, 2); + +static SENSOR_DEVICE_ATTR_2(in2_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 2, 0); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 2, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 2, 2); + +static SENSOR_DEVICE_ATTR_2(in3_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 3, 0); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 3, 1); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 3, 2); + +static SENSOR_DEVICE_ATTR_2(in4_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 4, 0); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 4, 1); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 4, 2); + +static SENSOR_DEVICE_ATTR_2(in5_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 5, 0); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 5, 1); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 5, 2); + +static SENSOR_DEVICE_ATTR_2(in6_input, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 6, 0); +static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 6, 1); +static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, + adc128_show_in, adc128_set_in, 6, 2); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adc128_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + adc128_show_temp, adc128_set_temp, 1); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, + adc128_show_temp, adc128_set_temp, 2); + +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, adc128_show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, adc128_show_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, adc128_show_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, adc128_show_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, adc128_show_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, adc128_show_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, adc128_show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adc128_show_alarm, NULL, 7); + +static struct attribute *adc128_attrs[] = { + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(adc128); + +static int adc128_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + int man_id, dev_id; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + man_id = i2c_smbus_read_byte_data(client, ADC128_REG_MAN_ID); + dev_id = i2c_smbus_read_byte_data(client, ADC128_REG_DEV_ID); + if (man_id != 0x01 || dev_id != 0x09) + return -ENODEV; + + /* Check unused bits for confirmation */ + if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG) & 0xf4) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_CONV_RATE) & 0xfe) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_ONESHOT) & 0xfe) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_SHUTDOWN) & 0xfe) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_CONFIG_ADV) & 0xf8) + return -ENODEV; + if (i2c_smbus_read_byte_data(client, ADC128_REG_BUSY_STATUS) & 0xfc) + return -ENODEV; + + strlcpy(info->type, "adc128d818", I2C_NAME_SIZE); + + return 0; +} + +static int adc128_init_client(struct adc128_data *data) +{ + struct i2c_client *client = data->client; + int err; + + /* + * Reset chip to defaults. + * This makes most other initializations unnecessary. + */ + err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x80); + if (err) + return err; + + /* Start monitoring */ + err = i2c_smbus_write_byte_data(client, ADC128_REG_CONFIG, 0x01); + if (err) + return err; + + /* If external vref is selected, configure the chip to use it */ + if (data->regulator) { + err = i2c_smbus_write_byte_data(client, + ADC128_REG_CONFIG_ADV, 0x01); + if (err) + return err; + } + + return 0; +} + +static int adc128_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct regulator *regulator; + struct device *hwmon_dev; + struct adc128_data *data; + int err, vref; + + data = devm_kzalloc(dev, sizeof(struct adc128_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* vref is optional. If specified, is used as chip reference voltage */ + regulator = devm_regulator_get_optional(dev, "vref"); + if (!IS_ERR(regulator)) { + data->regulator = regulator; + err = regulator_enable(regulator); + if (err < 0) + return err; + vref = regulator_get_voltage(regulator); + if (vref < 0) { + err = vref; + goto error; + } + data->vref = DIV_ROUND_CLOSEST(vref, 1000); + } else { + data->vref = 2560; /* 2.56V, in mV */ + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the chip */ + err = adc128_init_client(data); + if (err < 0) + goto error; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, adc128_groups); + if (IS_ERR(hwmon_dev)) { + err = PTR_ERR(hwmon_dev); + goto error; + } + + return 0; + +error: + if (data->regulator) + regulator_disable(data->regulator); + return err; +} + +static int adc128_remove(struct i2c_client *client) +{ + struct adc128_data *data = i2c_get_clientdata(client); + + if (data->regulator) + regulator_disable(data->regulator); + + return 0; +} + +static const struct i2c_device_id adc128_id[] = { + { "adc128d818", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adc128_id); + +static struct i2c_driver adc128_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "adc128d818", + }, + .probe = adc128_probe, + .remove = adc128_remove, + .id_table = adc128_id, + .detect = adc128_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(adc128_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("Driver for ADC128D818"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index bbb0b0d463f7..f31bc4c48644 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -94,6 +94,8 @@ struct temp_data { bool valid; struct sensor_device_attribute sd_attrs[TOTAL_ATTRS]; char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH]; + struct attribute *attrs[TOTAL_ATTRS + 1]; + struct attribute_group attr_group; struct mutex update_lock; }; @@ -114,12 +116,6 @@ struct pdev_entry { static LIST_HEAD(pdev_list); static DEFINE_MUTEX(pdev_list_mutex); -static ssize_t show_name(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - return sprintf(buf, "%s\n", DRVNAME); -} - static ssize_t show_label(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -393,20 +389,10 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev) return adjust_tjmax(c, id, dev); } -static int create_name_attr(struct platform_data *pdata, - struct device *dev) -{ - sysfs_attr_init(&pdata->name_attr.attr); - pdata->name_attr.attr.name = "name"; - pdata->name_attr.attr.mode = S_IRUGO; - pdata->name_attr.show = show_name; - return device_create_file(dev, &pdata->name_attr); -} - static int create_core_attrs(struct temp_data *tdata, struct device *dev, int attr_no) { - int err, i; + int i; static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev, struct device_attribute *devattr, char *buf) = { show_label, show_crit_alarm, show_temp, show_tjmax, @@ -424,16 +410,10 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev, tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; tdata->sd_attrs[i].index = attr_no; - err = device_create_file(dev, &tdata->sd_attrs[i].dev_attr); - if (err) - goto exit_free; + tdata->attrs[i] = &tdata->sd_attrs[i].dev_attr.attr; } - return 0; - -exit_free: - while (--i >= 0) - device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); - return err; + tdata->attr_group.attrs = tdata->attrs; + return sysfs_create_group(&dev->kobj, &tdata->attr_group); } @@ -548,7 +528,7 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, pdata->core_data[attr_no] = tdata; /* Create sysfs interfaces */ - err = create_core_attrs(tdata, &pdev->dev, attr_no); + err = create_core_attrs(tdata, pdata->hwmon_dev, attr_no); if (err) goto exit_free; @@ -573,14 +553,12 @@ static void coretemp_add_core(unsigned int cpu, int pkg_flag) } static void coretemp_remove_core(struct platform_data *pdata, - struct device *dev, int indx) + int indx) { - int i; struct temp_data *tdata = pdata->core_data[indx]; /* Remove the sysfs attributes */ - for (i = 0; i < tdata->attr_size; i++) - device_remove_file(dev, &tdata->sd_attrs[i].dev_attr); + sysfs_remove_group(&pdata->hwmon_dev->kobj, &tdata->attr_group); kfree(pdata->core_data[indx]); pdata->core_data[indx] = NULL; @@ -588,34 +566,20 @@ static void coretemp_remove_core(struct platform_data *pdata, static int coretemp_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct platform_data *pdata; - int err; /* Initialize the per-package data structures */ - pdata = kzalloc(sizeof(struct platform_data), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(struct platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; - err = create_name_attr(pdata, &pdev->dev); - if (err) - goto exit_free; - pdata->phys_proc_id = pdev->id; platform_set_drvdata(pdev, pdata); - pdata->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(pdata->hwmon_dev)) { - err = PTR_ERR(pdata->hwmon_dev); - dev_err(&pdev->dev, "Class registration failed (%d)\n", err); - goto exit_name; - } - return 0; - -exit_name: - device_remove_file(&pdev->dev, &pdata->name_attr); -exit_free: - kfree(pdata); - return err; + pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, + pdata, NULL); + return PTR_ERR_OR_ZERO(pdata->hwmon_dev); } static int coretemp_remove(struct platform_device *pdev) @@ -625,11 +589,8 @@ static int coretemp_remove(struct platform_device *pdev) for (i = MAX_CORE_DATA - 1; i >= 0; --i) if (pdata->core_data[i]) - coretemp_remove_core(pdata, &pdev->dev, i); + coretemp_remove_core(pdata, i); - device_remove_file(&pdev->dev, &pdata->name_attr); - hwmon_device_unregister(pdata->hwmon_dev); - kfree(pdata); return 0; } @@ -777,7 +738,7 @@ static void put_core_offline(unsigned int cpu) return; if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu) - coretemp_remove_core(pdata, &pdev->dev, indx); + coretemp_remove_core(pdata, indx); /* * If a HT sibling of a core is taken offline, but another HT sibling diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c index 2c137b26acb4..fd892dd48e4c 100644 --- a/drivers/hwmon/emc2103.c +++ b/drivers/hwmon/emc2103.c @@ -349,7 +349,7 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *da, dev_dbg(&client->dev, "reg 0x%02x, err %d\n", REG_FAN_CONF1, status); mutex_unlock(&data->update_lock); - return -EIO; + return status; } status &= 0x9F; status |= (new_range_bits << 5); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index e176a43af63d..a26c385a435b 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -22,6 +22,7 @@ #include <linux/gfp.h> #include <linux/spinlock.h> #include <linux/pci.h> +#include <linux/string.h> #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" @@ -99,6 +100,10 @@ hwmon_device_register_with_groups(struct device *dev, const char *name, struct hwmon_device *hwdev; int err, id; + /* Do not accept invalid characters in hwmon name attribute */ + if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) + return ERR_PTR(-EINVAL); + id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 708081b68c6f..9fbb1b1fdff3 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -31,6 +31,7 @@ struct iio_hwmon_state { int num_channels; struct device *hwmon_dev; struct attribute_group attr_group; + const struct attribute_group *groups[2]; struct attribute **attrs; }; @@ -56,19 +57,6 @@ static ssize_t iio_hwmon_read_val(struct device *dev, return sprintf(buf, "%d\n", result); } -static ssize_t show_name(struct device *dev, struct device_attribute *attr, - char *buf) -{ - const char *name = "iio_hwmon"; - - if (dev->of_node && dev->of_node->name) - name = dev->of_node->name; - - return sprintf(buf, "%s\n", name); -} - -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - static int iio_hwmon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -78,6 +66,10 @@ static int iio_hwmon_probe(struct platform_device *pdev) int in_i = 1, temp_i = 1, curr_i = 1; enum iio_chan_type type; struct iio_channel *channels; + const char *name = "iio_hwmon"; + + if (dev->of_node && dev->of_node->name) + name = dev->of_node->name; channels = iio_channel_get_all(dev); if (IS_ERR(channels)) @@ -96,7 +88,7 @@ static int iio_hwmon_probe(struct platform_device *pdev) st->num_channels++; st->attrs = devm_kzalloc(dev, - sizeof(*st->attrs) * (st->num_channels + 2), + sizeof(*st->attrs) * (st->num_channels + 1), GFP_KERNEL); if (st->attrs == NULL) { ret = -ENOMEM; @@ -144,22 +136,18 @@ static int iio_hwmon_probe(struct platform_device *pdev) a->index = i; st->attrs[i] = &a->dev_attr.attr; } - st->attrs[st->num_channels] = &dev_attr_name.attr; - st->attr_group.attrs = st->attrs; - platform_set_drvdata(pdev, st); - ret = sysfs_create_group(&dev->kobj, &st->attr_group); - if (ret < 0) - goto error_release_channels; - st->hwmon_dev = hwmon_device_register(dev); + st->attr_group.attrs = st->attrs; + st->groups[0] = &st->attr_group; + st->hwmon_dev = hwmon_device_register_with_groups(dev, name, st, + st->groups); if (IS_ERR(st->hwmon_dev)) { ret = PTR_ERR(st->hwmon_dev); - goto error_remove_group; + goto error_release_channels; } + platform_set_drvdata(pdev, st); return 0; -error_remove_group: - sysfs_remove_group(&dev->kobj, &st->attr_group); error_release_channels: iio_channel_release_all(channels); return ret; @@ -170,7 +158,6 @@ static int iio_hwmon_remove(struct platform_device *pdev) struct iio_hwmon_state *st = platform_get_drvdata(pdev); hwmon_device_unregister(st->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &st->attr_group); iio_channel_release_all(st->channels); return 0; diff --git a/drivers/hwmon/jz4740-hwmon.c b/drivers/hwmon/jz4740-hwmon.c index a183e488db78..7488e36809c8 100644 --- a/drivers/hwmon/jz4740-hwmon.c +++ b/drivers/hwmon/jz4740-hwmon.c @@ -28,7 +28,6 @@ #include <linux/hwmon.h> struct jz4740_hwmon { - struct resource *mem; void __iomem *base; int irq; @@ -106,6 +105,7 @@ static int jz4740_hwmon_probe(struct platform_device *pdev) { int ret; struct jz4740_hwmon *hwmon; + struct resource *mem; hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL); if (!hwmon) @@ -120,25 +120,10 @@ static int jz4740_hwmon_probe(struct platform_device *pdev) return hwmon->irq; } - hwmon->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!hwmon->mem) { - dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); - return -ENOENT; - } - - hwmon->mem = devm_request_mem_region(&pdev->dev, hwmon->mem->start, - resource_size(hwmon->mem), pdev->name); - if (!hwmon->mem) { - dev_err(&pdev->dev, "Failed to request mmio memory region\n"); - return -EBUSY; - } - - hwmon->base = devm_ioremap_nocache(&pdev->dev, hwmon->mem->start, - resource_size(hwmon->mem)); - if (!hwmon->base) { - dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); - return -EBUSY; - } + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hwmon->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(hwmon->base)) + return PTR_ERR(hwmon->base); init_completion(&hwmon->read_completion); mutex_init(&hwmon->lock); diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 4b68fb2a31d7..cdf19adaec79 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -89,7 +89,7 @@ static const u8 lm95241_reg_address[] = { /* Client data (each client gets its own) */ struct lm95241_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; unsigned long last_updated, interval; /* in jiffies */ char valid; /* zero until following fields are valid */ @@ -113,8 +113,8 @@ static int temp_from_reg_unsigned(u8 val_h, u8 val_l) static struct lm95241_data *lm95241_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; mutex_lock(&data->update_lock); @@ -122,7 +122,7 @@ static struct lm95241_data *lm95241_update_device(struct device *dev) !data->valid) { int i; - dev_dbg(&client->dev, "Updating lm95241 data.\n"); + dev_dbg(dev, "Updating lm95241 data.\n"); for (i = 0; i < ARRAY_SIZE(lm95241_reg_address); i++) data->temp[i] = i2c_smbus_read_byte_data(client, @@ -153,8 +153,7 @@ static ssize_t show_input(struct device *dev, struct device_attribute *attr, static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE - 1, data->model & to_sensor_dev_attr(attr)->index ? "1\n" : "2\n"); @@ -163,8 +162,8 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr, static ssize_t set_type(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int shift; u8 mask = to_sensor_dev_attr(attr)->index; @@ -201,8 +200,7 @@ static ssize_t set_type(struct device *dev, struct device_attribute *attr, static ssize_t show_min(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE - 1, data->config & to_sensor_dev_attr(attr)->index ? @@ -212,8 +210,7 @@ static ssize_t show_min(struct device *dev, struct device_attribute *attr, static ssize_t set_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); long val; if (kstrtol(buf, 10, &val) < 0) @@ -229,7 +226,8 @@ static ssize_t set_min(struct device *dev, struct device_attribute *attr, data->config &= ~to_sensor_dev_attr(attr)->index; data->valid = 0; - i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); + i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, + data->config); mutex_unlock(&data->update_lock); @@ -239,8 +237,7 @@ static ssize_t set_min(struct device *dev, struct device_attribute *attr, static ssize_t show_max(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE - 1, data->config & to_sensor_dev_attr(attr)->index ? @@ -250,8 +247,7 @@ static ssize_t show_max(struct device *dev, struct device_attribute *attr, static ssize_t set_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); long val; if (kstrtol(buf, 10, &val) < 0) @@ -267,7 +263,8 @@ static ssize_t set_max(struct device *dev, struct device_attribute *attr, data->config &= ~to_sensor_dev_attr(attr)->index; data->valid = 0; - i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); + i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG, + data->config); mutex_unlock(&data->update_lock); @@ -286,8 +283,7 @@ static ssize_t show_interval(struct device *dev, struct device_attribute *attr, static ssize_t set_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95241_data *data = i2c_get_clientdata(client); + struct lm95241_data *data = dev_get_drvdata(dev); unsigned long val; if (kstrtoul(buf, 10, &val) < 0) @@ -316,7 +312,7 @@ static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, set_interval); -static struct attribute *lm95241_attributes[] = { +static struct attribute *lm95241_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, @@ -329,10 +325,7 @@ static struct attribute *lm95241_attributes[] = { &dev_attr_update_interval.attr, NULL }; - -static const struct attribute_group lm95241_group = { - .attrs = lm95241_attributes, -}; +ATTRIBUTE_GROUPS(lm95241); /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm95241_detect(struct i2c_client *new_client, @@ -366,14 +359,11 @@ static int lm95241_detect(struct i2c_client *new_client, return 0; } -static void lm95241_init_client(struct i2c_client *client) +static void lm95241_init_client(struct i2c_client *client, + struct lm95241_data *data) { - struct lm95241_data *data = i2c_get_clientdata(client); - data->interval = HZ; /* 1 sec default */ - data->valid = 0; data->config = CFG_CR0076; - data->model = 0; data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT); i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config); @@ -385,49 +375,27 @@ static void lm95241_init_client(struct i2c_client *client) data->model); } -static int lm95241_probe(struct i2c_client *new_client, +static int lm95241_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct lm95241_data *data; - int err; + struct device *hwmon_dev; - data = devm_kzalloc(&new_client->dev, sizeof(struct lm95241_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct lm95241_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(new_client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the LM95241 chip */ - lm95241_init_client(new_client); + lm95241_init_client(client, data); - /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &lm95241_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm95241_group); - return err; -} - -static int lm95241_remove(struct i2c_client *client) -{ - struct lm95241_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm95241_group); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + lm95241_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } /* Driver data (common to all clients) */ @@ -444,7 +412,6 @@ static struct i2c_driver lm95241_driver = { .name = DEVNAME, }, .probe = lm95241_probe, - .remove = lm95241_remove, .id_table = lm95241_id, .detect = lm95241_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index a6c85f0ff8f3..0ae0dfdafdff 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -115,7 +115,7 @@ static const u8 lm95245_reg_address[] = { /* Client data (each client gets its own) */ struct lm95245_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; unsigned long last_updated; /* in jiffies */ unsigned long interval; /* in msecs */ @@ -140,8 +140,8 @@ static int temp_from_reg_signed(u8 val_h, u8 val_l) static struct lm95245_data *lm95245_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + struct lm95245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; mutex_lock(&data->update_lock); @@ -149,7 +149,6 @@ static struct lm95245_data *lm95245_update_device(struct device *dev) + msecs_to_jiffies(data->interval)) || !data->valid) { int i; - dev_dbg(&client->dev, "Updating lm95245 data.\n"); for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++) data->regs[i] = i2c_smbus_read_byte_data(client, @@ -249,9 +248,9 @@ static ssize_t show_limit(struct device *dev, struct device_attribute *attr, static ssize_t set_limit(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + struct lm95245_data *data = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; + struct i2c_client *client = data->client; unsigned long val; if (kstrtoul(buf, 10, &val) < 0) @@ -272,27 +271,38 @@ static ssize_t set_limit(struct device *dev, struct device_attribute *attr, return count; } +static ssize_t show_crit_hyst(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct lm95245_data *data = lm95245_update_device(dev); + int index = to_sensor_dev_attr(attr)->index; + int hyst = data->regs[index] - data->regs[8]; + + return snprintf(buf, PAGE_SIZE - 1, "%d\n", hyst * 1000); +} + static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + struct lm95245_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(attr)->index; + struct i2c_client *client = data->client; unsigned long val; + int hyst, limit; if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; - val /= 1000; - - val = clamp_val(val, 0, 31); - mutex_lock(&data->update_lock); - data->valid = 0; + limit = i2c_smbus_read_byte_data(client, lm95245_reg_address[index]); + hyst = limit - val / 1000; + hyst = clamp_val(hyst, 0, 31); + data->regs[8] = hyst; /* shared crit hysteresis */ i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS, - val); + hyst); mutex_unlock(&data->update_lock); @@ -302,8 +312,7 @@ static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr, static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + struct lm95245_data *data = dev_get_drvdata(dev); return snprintf(buf, PAGE_SIZE - 1, data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n"); @@ -312,8 +321,8 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr, static ssize_t set_type(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + struct lm95245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; if (kstrtoul(buf, 10, &val) < 0) @@ -359,8 +368,8 @@ static ssize_t show_interval(struct device *dev, struct device_attribute *attr, static ssize_t set_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct lm95245_data *data = i2c_get_clientdata(client); + struct lm95245_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; if (kstrtoul(buf, 10, &val) < 0) @@ -378,16 +387,15 @@ static ssize_t set_interval(struct device *dev, struct device_attribute *attr, static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit, set_limit, 6); -static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_limit, - set_crit_hyst, 8); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_crit_hyst, + set_crit_hyst, 6); static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, STATUS1_LOC); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2); static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit, set_limit, 7); -static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_limit, - set_crit_hyst, 8); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_crit_hyst, NULL, 7); static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, STATUS1_RTCRIT); static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, @@ -398,7 +406,7 @@ static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, set_interval); -static struct attribute *lm95245_attributes[] = { +static struct attribute *lm95245_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_crit.dev_attr.attr, &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, @@ -412,10 +420,7 @@ static struct attribute *lm95245_attributes[] = { &dev_attr_update_interval.attr, NULL }; - -static const struct attribute_group lm95245_group = { - .attrs = lm95245_attributes, -}; +ATTRIBUTE_GROUPS(lm95245); /* Return 0 if detection is successful, -ENODEV otherwise */ static int lm95245_detect(struct i2c_client *new_client, @@ -436,11 +441,9 @@ static int lm95245_detect(struct i2c_client *new_client, return 0; } -static void lm95245_init_client(struct i2c_client *client) +static void lm95245_init_client(struct i2c_client *client, + struct lm95245_data *data) { - struct lm95245_data *data = i2c_get_clientdata(client); - - data->valid = 0; data->interval = lm95245_read_conversion_rate(client); data->config1 = i2c_smbus_read_byte_data(client, @@ -456,49 +459,27 @@ static void lm95245_init_client(struct i2c_client *client) } } -static int lm95245_probe(struct i2c_client *new_client, +static int lm95245_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct lm95245_data *data; - int err; + struct device *hwmon_dev; - data = devm_kzalloc(&new_client->dev, sizeof(struct lm95245_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct lm95245_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(new_client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the LM95245 chip */ - lm95245_init_client(new_client); - - /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &lm95245_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } - - return 0; - -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &lm95245_group); - return err; -} + lm95245_init_client(client, data); -static int lm95245_remove(struct i2c_client *client) -{ - struct lm95245_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &lm95245_group); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + lm95245_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } /* Driver data (common to all clients) */ @@ -514,7 +495,6 @@ static struct i2c_driver lm95245_driver = { .name = DEVNAME, }, .probe = lm95245_probe, - .remove = lm95245_remove, .id_table = lm95245_id, .detect = lm95245_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c new file mode 100644 index 000000000000..c104cc32989d --- /dev/null +++ b/drivers/hwmon/ltc2945.c @@ -0,0 +1,519 @@ +/* + * Driver for Linear Technology LTC2945 I2C Power Monitor + * + * Copyright (c) 2014 Guenter Roeck + * + * 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/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/regmap.h> + +/* chip registers */ +#define LTC2945_CONTROL 0x00 +#define LTC2945_ALERT 0x01 +#define LTC2945_STATUS 0x02 +#define LTC2945_FAULT 0x03 +#define LTC2945_POWER_H 0x05 +#define LTC2945_MAX_POWER_H 0x08 +#define LTC2945_MIN_POWER_H 0x0b +#define LTC2945_MAX_POWER_THRES_H 0x0e +#define LTC2945_MIN_POWER_THRES_H 0x11 +#define LTC2945_SENSE_H 0x14 +#define LTC2945_MAX_SENSE_H 0x16 +#define LTC2945_MIN_SENSE_H 0x18 +#define LTC2945_MAX_SENSE_THRES_H 0x1a +#define LTC2945_MIN_SENSE_THRES_H 0x1c +#define LTC2945_VIN_H 0x1e +#define LTC2945_MAX_VIN_H 0x20 +#define LTC2945_MIN_VIN_H 0x22 +#define LTC2945_MAX_VIN_THRES_H 0x24 +#define LTC2945_MIN_VIN_THRES_H 0x26 +#define LTC2945_ADIN_H 0x28 +#define LTC2945_MAX_ADIN_H 0x2a +#define LTC2945_MIN_ADIN_H 0x2c +#define LTC2945_MAX_ADIN_THRES_H 0x2e +#define LTC2945_MIN_ADIN_THRES_H 0x30 +#define LTC2945_MIN_ADIN_THRES_L 0x31 + +/* Fault register bits */ + +#define FAULT_ADIN_UV (1 << 0) +#define FAULT_ADIN_OV (1 << 1) +#define FAULT_VIN_UV (1 << 2) +#define FAULT_VIN_OV (1 << 3) +#define FAULT_SENSE_UV (1 << 4) +#define FAULT_SENSE_OV (1 << 5) +#define FAULT_POWER_UV (1 << 6) +#define FAULT_POWER_OV (1 << 7) + +/* Control register bits */ + +#define CONTROL_MULT_SELECT (1 << 0) +#define CONTROL_TEST_MODE (1 << 4) + +static inline bool is_power_reg(u8 reg) +{ + return reg < LTC2945_SENSE_H; +} + +/* Return the value from the given register in uW, mV, or mA */ +static long long ltc2945_reg_to_val(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int control; + u8 buf[3]; + long long val; + int ret; + + ret = regmap_bulk_read(regmap, reg, buf, + is_power_reg(reg) ? 3 : 2); + if (ret < 0) + return ret; + + if (is_power_reg(reg)) { + /* power */ + val = (buf[0] << 16) + (buf[1] << 8) + buf[2]; + } else { + /* current, voltage */ + val = (buf[0] << 4) + (buf[1] >> 4); + } + + switch (reg) { + case LTC2945_POWER_H: + case LTC2945_MAX_POWER_H: + case LTC2945_MIN_POWER_H: + case LTC2945_MAX_POWER_THRES_H: + case LTC2945_MIN_POWER_THRES_H: + /* + * Convert to uW by assuming current is measured with + * an 1mOhm sense resistor, similar to current + * measurements. + * Control register bit 0 selects if voltage at SENSE+/VDD + * or voltage at ADIN is used to measure power. + */ + ret = regmap_read(regmap, LTC2945_CONTROL, &control); + if (ret < 0) + return ret; + if (control & CONTROL_MULT_SELECT) { + /* 25 mV * 25 uV = 0.625 uV resolution. */ + val *= 625LL; + } else { + /* 0.5 mV * 25 uV = 0.0125 uV resolution. */ + val = (val * 25LL) >> 1; + } + break; + case LTC2945_VIN_H: + case LTC2945_MAX_VIN_H: + case LTC2945_MIN_VIN_H: + case LTC2945_MAX_VIN_THRES_H: + case LTC2945_MIN_VIN_THRES_H: + /* 25 mV resolution. Convert to mV. */ + val *= 25; + break; + case LTC2945_ADIN_H: + case LTC2945_MAX_ADIN_H: + case LTC2945_MIN_ADIN_THRES_H: + case LTC2945_MAX_ADIN_THRES_H: + case LTC2945_MIN_ADIN_H: + /* 0.5mV resolution. Convert to mV. */ + val = val >> 1; + break; + case LTC2945_SENSE_H: + case LTC2945_MAX_SENSE_H: + case LTC2945_MIN_SENSE_H: + case LTC2945_MAX_SENSE_THRES_H: + case LTC2945_MIN_SENSE_THRES_H: + /* + * 25 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val *= 25; + break; + default: + return -EINVAL; + } + return val; +} + +static int ltc2945_val_to_reg(struct device *dev, u8 reg, + unsigned long val) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int control; + int ret; + + switch (reg) { + case LTC2945_POWER_H: + case LTC2945_MAX_POWER_H: + case LTC2945_MIN_POWER_H: + case LTC2945_MAX_POWER_THRES_H: + case LTC2945_MIN_POWER_THRES_H: + /* + * Convert to register value by assuming current is measured + * with an 1mOhm sense resistor, similar to current + * measurements. + * Control register bit 0 selects if voltage at SENSE+/VDD + * or voltage at ADIN is used to measure power, which in turn + * determines register calculations. + */ + ret = regmap_read(regmap, LTC2945_CONTROL, &control); + if (ret < 0) + return ret; + if (control & CONTROL_MULT_SELECT) { + /* 25 mV * 25 uV = 0.625 uV resolution. */ + val = DIV_ROUND_CLOSEST(val, 625); + } else { + /* + * 0.5 mV * 25 uV = 0.0125 uV resolution. + * Divide first to avoid overflow; + * accept loss of accuracy. + */ + val = DIV_ROUND_CLOSEST(val, 25) * 2; + } + break; + case LTC2945_VIN_H: + case LTC2945_MAX_VIN_H: + case LTC2945_MIN_VIN_H: + case LTC2945_MAX_VIN_THRES_H: + case LTC2945_MIN_VIN_THRES_H: + /* 25 mV resolution. */ + val /= 25; + break; + case LTC2945_ADIN_H: + case LTC2945_MAX_ADIN_H: + case LTC2945_MIN_ADIN_THRES_H: + case LTC2945_MAX_ADIN_THRES_H: + case LTC2945_MIN_ADIN_H: + /* 0.5mV resolution. */ + val *= 2; + break; + case LTC2945_SENSE_H: + case LTC2945_MAX_SENSE_H: + case LTC2945_MIN_SENSE_H: + case LTC2945_MAX_SENSE_THRES_H: + case LTC2945_MIN_SENSE_THRES_H: + /* + * 25 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = DIV_ROUND_CLOSEST(val, 25); + break; + default: + return -EINVAL; + } + return val; +} + +static ssize_t ltc2945_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + long long value; + + value = ltc2945_reg_to_val(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%lld\n", value); +} + +static ssize_t ltc2945_set_value(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + unsigned long val; + u8 regbuf[3]; + int num_regs; + int regval; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + /* convert to register value, then clamp and write result */ + regval = ltc2945_val_to_reg(dev, reg, val); + if (is_power_reg(reg)) { + regval = clamp_val(regval, 0, 0xffffff); + regbuf[0] = regval >> 16; + regbuf[1] = (regval >> 8) & 0xff; + regbuf[2] = regval; + num_regs = 3; + } else { + regval = clamp_val(regval, 0, 0xfff) << 4; + regbuf[0] = regval >> 8; + regbuf[1] = regval & 0xff; + num_regs = 2; + } + ret = regmap_bulk_write(regmap, reg, regbuf, num_regs); + return ret < 0 ? ret : count; +} + +static ssize_t ltc2945_reset_history(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + u8 reg = attr->index; + int num_regs = is_power_reg(reg) ? 3 : 2; + u8 buf_min[3] = { 0xff, 0xff, 0xff }; + u8 buf_max[3] = { 0, 0, 0 }; + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + if (val != 1) + return -EINVAL; + + ret = regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, + CONTROL_TEST_MODE); + + /* Reset minimum */ + ret = regmap_bulk_write(regmap, reg, buf_min, num_regs); + if (ret) + return ret; + + switch (reg) { + case LTC2945_MIN_POWER_H: + reg = LTC2945_MAX_POWER_H; + break; + case LTC2945_MIN_SENSE_H: + reg = LTC2945_MAX_SENSE_H; + break; + case LTC2945_MIN_VIN_H: + reg = LTC2945_MAX_VIN_H; + break; + case LTC2945_MIN_ADIN_H: + reg = LTC2945_MAX_ADIN_H; + break; + default: + BUG(); + break; + } + /* Reset maximum */ + ret = regmap_bulk_write(regmap, reg, buf_max, num_regs); + + /* Try resetting test mode even if there was an error */ + regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, 0); + + return ret ? : count; +} + +static ssize_t ltc2945_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, LTC2945_FAULT, &fault); + if (ret < 0) + return ret; + + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Input voltages */ + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_VIN_H); +static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_VIN_THRES_H); +static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_VIN_THRES_H); +static SENSOR_DEVICE_ATTR(in1_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_VIN_H); +static SENSOR_DEVICE_ATTR(in1_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_VIN_H); +static SENSOR_DEVICE_ATTR(in1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_VIN_H); + +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_ADIN_THRES_H); +static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_ADIN_THRES_H); +static SENSOR_DEVICE_ATTR(in2_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_ADIN_H); +static SENSOR_DEVICE_ATTR(in2_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_ADIN_H); + +/* Voltage alarms */ + +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_VIN_UV); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_VIN_OV); +static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_ADIN_UV); +static SENSOR_DEVICE_ATTR(in2_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_ADIN_OV); + +/* Currents (via sense resistor) */ + +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_SENSE_THRES_H); +static SENSOR_DEVICE_ATTR(curr1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_SENSE_THRES_H); +static SENSOR_DEVICE_ATTR(curr1_lowest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MIN_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_highest, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_MAX_SENSE_H); +static SENSOR_DEVICE_ATTR(curr1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_SENSE_H); + +/* Current alarms */ + +static SENSOR_DEVICE_ATTR(curr1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_SENSE_UV); +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_SENSE_OV); + +/* Power */ + +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc2945_show_value, NULL, + LTC2945_POWER_H); +static SENSOR_DEVICE_ATTR(power1_min, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MIN_POWER_THRES_H); +static SENSOR_DEVICE_ATTR(power1_max, S_IRUGO | S_IWUSR, ltc2945_show_value, + ltc2945_set_value, LTC2945_MAX_POWER_THRES_H); +static SENSOR_DEVICE_ATTR(power1_input_lowest, S_IRUGO, ltc2945_show_value, + NULL, LTC2945_MIN_POWER_H); +static SENSOR_DEVICE_ATTR(power1_input_highest, S_IRUGO, ltc2945_show_value, + NULL, LTC2945_MAX_POWER_H); +static SENSOR_DEVICE_ATTR(power1_reset_history, S_IWUSR, NULL, + ltc2945_reset_history, LTC2945_MIN_POWER_H); + +/* Power alarms */ + +static SENSOR_DEVICE_ATTR(power1_min_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_POWER_UV); +static SENSOR_DEVICE_ATTR(power1_max_alarm, S_IRUGO, ltc2945_show_bool, NULL, + FAULT_POWER_OV); + +static struct attribute *ltc2945_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_lowest.dev_attr.attr, + &sensor_dev_attr_in1_highest.dev_attr.attr, + &sensor_dev_attr_in1_reset_history.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_lowest.dev_attr.attr, + &sensor_dev_attr_in2_highest.dev_attr.attr, + &sensor_dev_attr_in2_reset_history.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, + &sensor_dev_attr_in2_max_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_min.dev_attr.attr, + &sensor_dev_attr_curr1_max.dev_attr.attr, + &sensor_dev_attr_curr1_lowest.dev_attr.attr, + &sensor_dev_attr_curr1_highest.dev_attr.attr, + &sensor_dev_attr_curr1_reset_history.dev_attr.attr, + &sensor_dev_attr_curr1_min_alarm.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power1_min.dev_attr.attr, + &sensor_dev_attr_power1_max.dev_attr.attr, + &sensor_dev_attr_power1_input_lowest.dev_attr.attr, + &sensor_dev_attr_power1_input_highest.dev_attr.attr, + &sensor_dev_attr_power1_reset_history.dev_attr.attr, + &sensor_dev_attr_power1_min_alarm.dev_attr.attr, + &sensor_dev_attr_power1_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc2945); + +static struct regmap_config ltc2945_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC2945_MIN_ADIN_THRES_L, +}; + +static int ltc2945_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c2945_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC2945_FAULT, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc2945_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc2945_id[] = { + {"ltc2945", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc2945_id); + +static struct i2c_driver ltc2945_driver = { + .driver = { + .name = "ltc2945", + }, + .probe = ltc2945_probe, + .id_table = ltc2945_id, +}; + +module_i2c_driver(ltc2945_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("LTC2945 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index 8a142960d69e..c8a9bd9b050f 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -33,7 +33,7 @@ enum ltc4215_cmd { }; struct ltc4215_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; bool valid; @@ -45,8 +45,8 @@ struct ltc4215_data { static struct ltc4215_data *ltc4215_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct ltc4215_data *data = i2c_get_clientdata(client); + struct ltc4215_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; s32 val; int i; @@ -214,7 +214,7 @@ static SENSOR_DEVICE_ATTR(in2_min_alarm, S_IRUGO, ltc4215_show_alarm, NULL, * Finally, construct an array of pointers to members of the above objects, * as required for sysfs_create_group() */ -static struct attribute *ltc4215_attributes[] = { +static struct attribute *ltc4215_attrs[] = { &sensor_dev_attr_curr1_input.dev_attr.attr, &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, @@ -229,57 +229,33 @@ static struct attribute *ltc4215_attributes[] = { NULL, }; - -static const struct attribute_group ltc4215_group = { - .attrs = ltc4215_attributes, -}; +ATTRIBUTE_GROUPS(ltc4215); static int ltc4215_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; struct ltc4215_data *data; - int ret; + struct device *hwmon_dev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the LTC4215 chip */ i2c_smbus_write_byte_data(client, LTC4215_FAULT, 0x00); - /* Register sysfs hooks */ - ret = sysfs_create_group(&client->dev.kobj, <c4215_group); - if (ret) - return ret; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - goto out_hwmon_device_register; - } - - return 0; - -out_hwmon_device_register: - sysfs_remove_group(&client->dev.kobj, <c4215_group); - return ret; -} - -static int ltc4215_remove(struct i2c_client *client) -{ - struct ltc4215_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, <c4215_group); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + ltc4215_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ltc4215_id[] = { @@ -294,7 +270,6 @@ static struct i2c_driver ltc4215_driver = { .name = "ltc4215", }, .probe = ltc4215_probe, - .remove = ltc4215_remove, .id_table = ltc4215_id, }; diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c new file mode 100644 index 000000000000..07c25653659f --- /dev/null +++ b/drivers/hwmon/ltc4222.c @@ -0,0 +1,237 @@ +/* + * Driver for Linear Technology LTC4222 Dual Hot Swap controller + * + * Copyright (c) 2014 Guenter Roeck + * + * 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/err.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/regmap.h> + +/* chip registers */ + +#define LTC4222_CONTROL1 0xd0 +#define LTC4222_ALERT1 0xd1 +#define LTC4222_STATUS1 0xd2 +#define LTC4222_FAULT1 0xd3 +#define LTC4222_CONTROL2 0xd4 +#define LTC4222_ALERT2 0xd5 +#define LTC4222_STATUS2 0xd6 +#define LTC4222_FAULT2 0xd7 +#define LTC4222_SOURCE1 0xd8 +#define LTC4222_SOURCE2 0xda +#define LTC4222_ADIN1 0xdc +#define LTC4222_ADIN2 0xde +#define LTC4222_SENSE1 0xe0 +#define LTC4222_SENSE2 0xe2 +#define LTC4222_ADC_CONTROL 0xe4 + +/* + * Fault register bits + */ +#define FAULT_OV BIT(0) +#define FAULT_UV BIT(1) +#define FAULT_OC BIT(2) +#define FAULT_POWER_BAD BIT(3) +#define FAULT_FET_BAD BIT(5) + +/* Return the voltage from the given register in mV or mA */ +static int ltc4222_get_value(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int val; + u8 buf[2]; + int ret; + + ret = regmap_bulk_read(regmap, reg, buf, 2); + if (ret < 0) + return ret; + + val = ((buf[0] << 8) + buf[1]) >> 6; + + switch (reg) { + case LTC4222_ADIN1: + case LTC4222_ADIN2: + /* 1.25 mV resolution. Convert to mV. */ + val = DIV_ROUND_CLOSEST(val * 5, 4); + break; + case LTC4222_SOURCE1: + case LTC4222_SOURCE2: + /* 31.25 mV resolution. Convert to mV. */ + val = DIV_ROUND_CLOSEST(val * 125, 4); + break; + case LTC4222_SENSE1: + case LTC4222_SENSE2: + /* + * 62.5 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = DIV_ROUND_CLOSEST(val * 125, 2); + break; + default: + return -EINVAL; + } + return val; +} + +static ssize_t ltc4222_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int value; + + value = ltc4222_get_value(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t ltc4222_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, attr->nr, &fault); + if (ret < 0) + return ret; + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, attr->nr, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Voltages */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SOURCE1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_ADIN1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SOURCE2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_ADIN2); + +/* + * Voltage alarms + * UV/OV faults are associated with the input voltage, and power bad and fet + * faults are associated with the output voltage. + */ +static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_UV); +static SENSOR_DEVICE_ATTR_2(in1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_OV); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_POWER_BAD | FAULT_FET_BAD); + +static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_UV); +static SENSOR_DEVICE_ATTR_2(in3_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_OV); +static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_POWER_BAD | FAULT_FET_BAD); + +/* Current (via sense resistor) */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SENSE1); +static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4222_show_value, NULL, + LTC4222_SENSE2); + +/* Overcurrent alarm */ +static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT1, FAULT_OC); +static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4222_show_bool, NULL, + LTC4222_FAULT2, FAULT_OC); + +static struct attribute *ltc4222_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_min_alarm.dev_attr.attr, + &sensor_dev_attr_in3_max_alarm.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + &sensor_dev_attr_curr2_input.dev_attr.attr, + &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc4222); + +static struct regmap_config ltc4222_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC4222_ADC_CONTROL, +}; + +static int ltc4222_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c4222_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC4222_FAULT1, 0x00); + regmap_write(regmap, LTC4222_FAULT2, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc4222_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc4222_id[] = { + {"ltc4222", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc4222_id); + +static struct i2c_driver ltc4222_driver = { + .driver = { + .name = "ltc4222", + }, + .probe = ltc4222_probe, + .id_table = ltc4222_id, +}; + +module_i2c_driver(ltc4222_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("LTC4222 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c index d4172933ce4f..681b5b7b3c3b 100644 --- a/drivers/hwmon/ltc4245.c +++ b/drivers/hwmon/ltc4245.c @@ -95,7 +95,6 @@ static void ltc4245_update_gpios(struct device *dev) * readings as stale by setting them to -EAGAIN */ if (time_after(jiffies, data->last_updated + 5 * HZ)) { - dev_dbg(&client->dev, "Marking GPIOs invalid\n"); for (i = 0; i < ARRAY_SIZE(data->gpios); i++) data->gpios[i] = -EAGAIN; } @@ -141,8 +140,6 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - dev_dbg(&client->dev, "Starting ltc4245 update\n"); - /* Read control registers -- 0x00 to 0x07 */ for (i = 0; i < ARRAY_SIZE(data->cregs); i++) { val = i2c_smbus_read_byte_data(client, i); @@ -470,19 +467,15 @@ static void ltc4245_sysfs_add_groups(struct ltc4245_data *data) static bool ltc4245_use_extra_gpios(struct i2c_client *client) { struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev); -#ifdef CONFIG_OF struct device_node *np = client->dev.of_node; -#endif /* prefer platform data */ if (pdata) return pdata->use_extra_gpios; -#ifdef CONFIG_OF /* fallback on OF */ if (of_find_property(np, "ltc4245,use-extra-gpios", NULL)) return true; -#endif return false; } @@ -512,24 +505,10 @@ static int ltc4245_probe(struct i2c_client *client, /* Add sysfs hooks */ ltc4245_sysfs_add_groups(data); - hwmon_dev = hwmon_device_register_with_groups(&client->dev, - client->name, data, - data->groups); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - i2c_set_clientdata(client, hwmon_dev); - - return 0; -} - -static int ltc4245_remove(struct i2c_client *client) -{ - struct device *hwmon_dev = i2c_get_clientdata(client); - - hwmon_device_unregister(hwmon_dev); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id ltc4245_id[] = { @@ -544,7 +523,6 @@ static struct i2c_driver ltc4245_driver = { .name = "ltc4245", }, .probe = ltc4245_probe, - .remove = ltc4245_remove, .id_table = ltc4245_id, }; diff --git a/drivers/hwmon/ltc4260.c b/drivers/hwmon/ltc4260.c new file mode 100644 index 000000000000..453a250d9df5 --- /dev/null +++ b/drivers/hwmon/ltc4260.c @@ -0,0 +1,200 @@ +/* + * Driver for Linear Technology LTC4260 I2C Positive Voltage Hot Swap Controller + * + * Copyright (c) 2014 Guenter Roeck + * + * 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/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/regmap.h> + +/* chip registers */ +#define LTC4260_CONTROL 0x00 +#define LTC4260_ALERT 0x01 +#define LTC4260_STATUS 0x02 +#define LTC4260_FAULT 0x03 +#define LTC4260_SENSE 0x04 +#define LTC4260_SOURCE 0x05 +#define LTC4260_ADIN 0x06 + +/* + * Fault register bits + */ +#define FAULT_OV (1 << 0) +#define FAULT_UV (1 << 1) +#define FAULT_OC (1 << 2) +#define FAULT_POWER_BAD (1 << 3) +#define FAULT_FET_SHORT (1 << 5) + +/* Return the voltage from the given register in mV or mA */ +static int ltc4260_get_value(struct device *dev, u8 reg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = regmap_read(regmap, reg, &val); + if (ret < 0) + return ret; + + switch (reg) { + case LTC4260_ADIN: + /* 10 mV resolution. Convert to mV. */ + val = val * 10; + break; + case LTC4260_SOURCE: + /* 400 mV resolution. Convert to mV. */ + val = val * 400; + break; + case LTC4260_SENSE: + /* + * 300 uV resolution. Convert to current as measured with + * an 1 mOhm sense resistor, in mA. If a different sense + * resistor is installed, calculate the actual current by + * dividing the reported current by the sense resistor value + * in mOhm. + */ + val = val * 300; + break; + default: + return -EINVAL; + } + + return val; +} + +static ssize_t ltc4260_show_value(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + int value; + + value = ltc4260_get_value(dev, attr->index); + if (value < 0) + return value; + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t ltc4260_show_bool(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct regmap *regmap = dev_get_drvdata(dev); + unsigned int fault; + int ret; + + ret = regmap_read(regmap, LTC4260_FAULT, &fault); + if (ret < 0) + return ret; + + fault &= attr->index; + if (fault) /* Clear reported faults in chip register */ + regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0); + + return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); +} + +/* Voltages */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4260_show_value, NULL, + LTC4260_SOURCE); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4260_show_value, NULL, + LTC4260_ADIN); + +/* + * Voltage alarms + * UV/OV faults are associated with the input voltage, and the POWER BAD and + * FET SHORT faults are associated with the output voltage. + */ +static SENSOR_DEVICE_ATTR(in1_min_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_UV); +static SENSOR_DEVICE_ATTR(in1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_OV); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_POWER_BAD | FAULT_FET_SHORT); + +/* Current (via sense resistor) */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4260_show_value, NULL, + LTC4260_SENSE); + +/* Overcurrent alarm */ +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, ltc4260_show_bool, NULL, + FAULT_OC); + +static struct attribute *ltc4260_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ltc4260); + +static struct regmap_config ltc4260_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LTC4260_ADIN, +}; + +static int ltc4260_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, <c4260_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + /* Clear faults */ + regmap_write(regmap, LTC4260_FAULT, 0x00); + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + regmap, + ltc4260_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id ltc4260_id[] = { + {"ltc4260", 0}, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ltc4260_id); + +static struct i2c_driver ltc4260_driver = { + .driver = { + .name = "ltc4260", + }, + .probe = ltc4260_probe, + .id_table = ltc4260_id, +}; + +module_i2c_driver(ltc4260_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("LTC4260 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index 029b65e6c589..e3ed0a5b6d94 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -66,7 +66,8 @@ MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); enum chips { max1668, max1805, max1989 }; struct max1668_data { - struct device *hwmon_dev; + struct i2c_client *client; + const struct attribute_group *groups[3]; enum chips type; struct mutex update_lock; @@ -82,8 +83,8 @@ struct max1668_data { static struct max1668_data *max1668_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct max1668_data *data = i2c_get_clientdata(client); + struct max1668_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct max1668_data *ret = data; s32 val; int i; @@ -205,8 +206,8 @@ static ssize_t set_temp_max(struct device *dev, const char *buf, size_t count) { int index = to_sensor_dev_attr(devattr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct max1668_data *data = i2c_get_clientdata(client); + struct max1668_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long temp; int ret; @@ -216,10 +217,11 @@ static ssize_t set_temp_max(struct device *dev, mutex_lock(&data->update_lock); data->temp_max[index] = clamp_val(temp/1000, -128, 127); - if (i2c_smbus_write_byte_data(client, + ret = i2c_smbus_write_byte_data(client, MAX1668_REG_LIMH_WR(index), - data->temp_max[index])) - count = -EIO; + data->temp_max[index]); + if (ret < 0) + count = ret; mutex_unlock(&data->update_lock); return count; @@ -230,8 +232,8 @@ static ssize_t set_temp_min(struct device *dev, const char *buf, size_t count) { int index = to_sensor_dev_attr(devattr)->index; - struct i2c_client *client = to_i2c_client(dev); - struct max1668_data *data = i2c_get_clientdata(client); + struct max1668_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long temp; int ret; @@ -241,10 +243,11 @@ static ssize_t set_temp_min(struct device *dev, mutex_lock(&data->update_lock); data->temp_min[index] = clamp_val(temp/1000, -128, 127); - if (i2c_smbus_write_byte_data(client, + ret = i2c_smbus_write_byte_data(client, MAX1668_REG_LIML_WR(index), - data->temp_min[index])) - count = -EIO; + data->temp_min[index]); + if (ret < 0) + count = ret; mutex_unlock(&data->update_lock); return count; @@ -405,60 +408,29 @@ static int max1668_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct device *hwmon_dev; struct max1668_data *data; - int err; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - data = devm_kzalloc(&client->dev, sizeof(struct max1668_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct max1668_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; data->type = id->driver_data; mutex_init(&data->update_lock); - /* Register sysfs hooks */ - err = sysfs_create_group(&client->dev.kobj, &max1668_group_common); - if (err) - return err; - - if (data->type == max1668 || data->type == max1989) { - err = sysfs_create_group(&client->dev.kobj, - &max1668_group_unique); - if (err) - goto error_sysrem0; - } - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error_sysrem1; - } - - return 0; - -error_sysrem1: + /* sysfs hooks */ + data->groups[0] = &max1668_group_common; if (data->type == max1668 || data->type == max1989) - sysfs_remove_group(&client->dev.kobj, &max1668_group_unique); -error_sysrem0: - sysfs_remove_group(&client->dev.kobj, &max1668_group_common); - return err; -} - -static int max1668_remove(struct i2c_client *client) -{ - struct max1668_data *data = i2c_get_clientdata(client); + data->groups[1] = &max1668_group_unique; - hwmon_device_unregister(data->hwmon_dev); - if (data->type == max1668 || data->type == max1989) - sysfs_remove_group(&client->dev.kobj, &max1668_group_unique); - - sysfs_remove_group(&client->dev.kobj, &max1668_group_common); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct i2c_device_id max1668_id[] = { @@ -476,7 +448,6 @@ static struct i2c_driver max1668_driver = { .name = "max1668", }, .probe = max1668_probe, - .remove = max1668_remove, .id_table = max1668_id, .detect = max1668_detect, .address_list = max1668_addr_list, diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 066e587a18a5..70650de2cbd1 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -80,7 +80,7 @@ static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 }; * Client data (each client gets its own) */ struct max6639_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -104,8 +104,8 @@ struct max6639_data { static struct max6639_data *max6639_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct max6639_data *ret = data; int i; int status_reg; @@ -191,9 +191,8 @@ static ssize_t show_temp_fault(struct device *dev, static ssize_t show_temp_max(struct device *dev, struct device_attribute *dev_attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", (data->temp_therm[attr->index] * 1000)); } @@ -202,9 +201,9 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *dev_attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int res; @@ -224,9 +223,8 @@ static ssize_t set_temp_max(struct device *dev, static ssize_t show_temp_crit(struct device *dev, struct device_attribute *dev_attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", (data->temp_alert[attr->index] * 1000)); } @@ -235,9 +233,9 @@ static ssize_t set_temp_crit(struct device *dev, struct device_attribute *dev_attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int res; @@ -258,9 +256,8 @@ static ssize_t show_temp_emergency(struct device *dev, struct device_attribute *dev_attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", (data->temp_ot[attr->index] * 1000)); } @@ -269,9 +266,9 @@ static ssize_t set_temp_emergency(struct device *dev, struct device_attribute *dev_attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int res; @@ -291,9 +288,8 @@ static ssize_t set_temp_emergency(struct device *dev, static ssize_t show_pwm(struct device *dev, struct device_attribute *dev_attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", data->pwm[attr->index] * 255 / 120); } @@ -302,9 +298,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *dev_attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6639_data *data = i2c_get_clientdata(client); struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct max6639_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; int res; @@ -378,7 +374,7 @@ static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_alarm, NULL, 5); static SENSOR_DEVICE_ATTR(temp2_emergency_alarm, S_IRUGO, show_alarm, NULL, 4); -static struct attribute *max6639_attributes[] = { +static struct attribute *max6639_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp1_fault.dev_attr.attr, @@ -403,10 +399,7 @@ static struct attribute *max6639_attributes[] = { &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr, NULL }; - -static const struct attribute_group max6639_group = { - .attrs = max6639_attributes, -}; +ATTRIBUTE_GROUPS(max6639); /* * returns respective index in rpm_ranges table @@ -424,9 +417,9 @@ static int rpm_range_to_reg(int range) return 1; /* default: 4000 RPM */ } -static int max6639_init_client(struct i2c_client *client) +static int max6639_init_client(struct i2c_client *client, + struct max6639_data *data) { - struct max6639_data *data = i2c_get_clientdata(client); struct max6639_platform_data *max6639_info = dev_get_platdata(&client->dev); int i; @@ -545,50 +538,27 @@ static int max6639_detect(struct i2c_client *client, static int max6639_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct max6639_data *data; + struct device *hwmon_dev; int err; - data = devm_kzalloc(&client->dev, sizeof(struct max6639_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(struct max6639_data), GFP_KERNEL); if (!data) return -ENOMEM; - i2c_set_clientdata(client, data); + data->client = client; mutex_init(&data->update_lock); /* Initialize the max6639 chip */ - err = max6639_init_client(client); + err = max6639_init_client(client, data); if (err < 0) return err; - /* Register sysfs hooks */ - err = sysfs_create_group(&client->dev.kobj, &max6639_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error_remove; - } - - dev_info(&client->dev, "temperature sensor and fan control found\n"); - - return 0; - -error_remove: - sysfs_remove_group(&client->dev.kobj, &max6639_group); - return err; -} - -static int max6639_remove(struct i2c_client *client) -{ - struct max6639_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &max6639_group); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + max6639_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } #ifdef CONFIG_PM_SLEEP @@ -622,9 +592,7 @@ static const struct i2c_device_id max6639_id[] = { MODULE_DEVICE_TABLE(i2c, max6639_id); -static const struct dev_pm_ops max6639_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(max6639_suspend, max6639_resume) -}; +static SIMPLE_DEV_PM_OPS(max6639_pm_ops, max6639_suspend, max6639_resume); static struct i2c_driver max6639_driver = { .class = I2C_CLASS_HWMON, @@ -633,7 +601,6 @@ static struct i2c_driver max6639_driver = { .pm = &max6639_pm_ops, }, .probe = max6639_probe, - .remove = max6639_remove, .id_table = max6639_id, .detect = max6639_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 0cafc390db4d..162a520f4bd6 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -105,38 +105,13 @@ module_param(clock, int, S_IRUGO); #define DIV_FROM_REG(reg) (1 << (reg & 7)) -static int max6650_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int max6650_init_client(struct i2c_client *client); -static int max6650_remove(struct i2c_client *client); -static struct max6650_data *max6650_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id max6650_id[] = { - { "max6650", 1 }, - { "max6651", 4 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max6650_id); - -static struct i2c_driver max6650_driver = { - .driver = { - .name = "max6650", - }, - .probe = max6650_probe, - .remove = max6650_remove, - .id_table = max6650_id, -}; - /* * Client data (each client gets its own) */ struct max6650_data { - struct device *hwmon_dev; + struct i2c_client *client; + const struct attribute_group *groups[3]; struct mutex update_lock; int nr_fans; char valid; /* zero until following fields are valid */ @@ -151,6 +126,51 @@ struct max6650_data { u8 alarm; }; +static const u8 tach_reg[] = { + MAX6650_REG_TACH0, + MAX6650_REG_TACH1, + MAX6650_REG_TACH2, + MAX6650_REG_TACH3, +}; + +static struct max6650_data *max6650_update_device(struct device *dev) +{ + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + data->speed = i2c_smbus_read_byte_data(client, + MAX6650_REG_SPEED); + data->config = i2c_smbus_read_byte_data(client, + MAX6650_REG_CONFIG); + for (i = 0; i < data->nr_fans; i++) { + data->tach[i] = i2c_smbus_read_byte_data(client, + tach_reg[i]); + } + data->count = i2c_smbus_read_byte_data(client, + MAX6650_REG_COUNT); + data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC); + + /* + * Alarms are cleared on read in case the condition that + * caused the alarm is removed. Keep the value latched here + * for providing the register through different alarm files. + */ + data->alarm |= i2c_smbus_read_byte_data(client, + MAX6650_REG_ALARM); + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -235,8 +255,8 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr, static ssize_t set_target(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int kscale, ktach; unsigned long rpm; int err; @@ -304,8 +324,8 @@ static ssize_t get_pwm(struct device *dev, struct device_attribute *devattr, static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long pwm; int err; @@ -350,8 +370,8 @@ static ssize_t get_enable(struct device *dev, struct device_attribute *devattr, static ssize_t set_enable(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int max6650_modes[3] = {0, 3, 2}; unsigned long mode; int err; @@ -400,8 +420,8 @@ static ssize_t get_div(struct device *dev, struct device_attribute *devattr, static ssize_t set_div(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long div; int err; @@ -446,7 +466,7 @@ static ssize_t get_alarm(struct device *dev, struct device_attribute *devattr, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max6650_data *data = max6650_update_device(dev); - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = data->client; int alarm = 0; if (data->alarm & attr->index) { @@ -484,7 +504,8 @@ static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, int n) { struct device *dev = container_of(kobj, struct device, kobj); - struct i2c_client *client = to_i2c_client(dev); + struct max6650_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u8 alarm_en = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN); struct device_attribute *devattr; @@ -519,7 +540,7 @@ static struct attribute *max6650_attrs[] = { NULL }; -static struct attribute_group max6650_attr_grp = { +static const struct attribute_group max6650_group = { .attrs = max6650_attrs, .is_visible = max6650_attrs_visible, }; @@ -531,7 +552,7 @@ static struct attribute *max6651_attrs[] = { NULL }; -static const struct attribute_group max6651_attr_grp = { +static const struct attribute_group max6651_group = { .attrs = max6651_attrs, }; @@ -539,74 +560,17 @@ static const struct attribute_group max6651_attr_grp = { * Real code */ -static int max6650_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct max6650_data *data; - int err; - - data = devm_kzalloc(&client->dev, sizeof(struct max6650_data), - GFP_KERNEL); - if (!data) { - dev_err(&client->dev, "out of memory.\n"); - return -ENOMEM; - } - - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); - data->nr_fans = id->driver_data; - - /* - * Initialize the max6650 chip - */ - err = max6650_init_client(client); - if (err) - return err; - - err = sysfs_create_group(&client->dev.kobj, &max6650_attr_grp); - if (err) - return err; - /* 3 additional fan inputs for the MAX6651 */ - if (data->nr_fans == 4) { - err = sysfs_create_group(&client->dev.kobj, &max6651_attr_grp); - if (err) - goto err_remove; - } - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (!IS_ERR(data->hwmon_dev)) - return 0; - - err = PTR_ERR(data->hwmon_dev); - dev_err(&client->dev, "error registering hwmon device.\n"); - if (data->nr_fans == 4) - sysfs_remove_group(&client->dev.kobj, &max6651_attr_grp); -err_remove: - sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); - return err; -} - -static int max6650_remove(struct i2c_client *client) +static int max6650_init_client(struct max6650_data *data, + struct i2c_client *client) { - struct max6650_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - if (data->nr_fans == 4) - sysfs_remove_group(&client->dev.kobj, &max6651_attr_grp); - sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp); - return 0; -} - -static int max6650_init_client(struct i2c_client *client) -{ - struct max6650_data *data = i2c_get_clientdata(client); + struct device *dev = &client->dev; int config; int err = -EIO; config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); if (config < 0) { - dev_err(&client->dev, "Error reading config, aborting.\n"); + dev_err(dev, "Error reading config, aborting.\n"); return err; } @@ -620,11 +584,11 @@ static int max6650_init_client(struct i2c_client *client) config |= MAX6650_CFG_V12; break; default: - dev_err(&client->dev, "illegal value for fan_voltage (%d)\n", + dev_err(dev, "illegal value for fan_voltage (%d)\n", fan_voltage); } - dev_info(&client->dev, "Fan voltage is set to %dV.\n", + dev_info(dev, "Fan voltage is set to %dV.\n", (config & MAX6650_CFG_V12) ? 12 : 5); switch (prescaler) { @@ -650,11 +614,10 @@ static int max6650_init_client(struct i2c_client *client) | MAX6650_CFG_PRESCALER_16; break; default: - dev_err(&client->dev, "illegal value for prescaler (%d)\n", - prescaler); + dev_err(dev, "illegal value for prescaler (%d)\n", prescaler); } - dev_info(&client->dev, "Prescaler is set to %d.\n", + dev_info(dev, "Prescaler is set to %d.\n", 1 << (config & MAX6650_CFG_PRESCALER_MASK)); /* @@ -664,17 +627,17 @@ static int max6650_init_client(struct i2c_client *client) */ if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) { - dev_dbg(&client->dev, "Change mode to open loop, full off.\n"); + dev_dbg(dev, "Change mode to open loop, full off.\n"); config = (config & ~MAX6650_CFG_MODE_MASK) | MAX6650_CFG_MODE_OPEN_LOOP; if (i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, 255)) { - dev_err(&client->dev, "DAC write error, aborting.\n"); + dev_err(dev, "DAC write error, aborting.\n"); return err; } } if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) { - dev_err(&client->dev, "Config write error, aborting.\n"); + dev_err(dev, "Config write error, aborting.\n"); return err; } @@ -684,51 +647,55 @@ static int max6650_init_client(struct i2c_client *client) return 0; } -static const u8 tach_reg[] = { - MAX6650_REG_TACH0, - MAX6650_REG_TACH1, - MAX6650_REG_TACH2, - MAX6650_REG_TACH3, -}; - -static struct max6650_data *max6650_update_device(struct device *dev) +static int max6650_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - int i; - struct i2c_client *client = to_i2c_client(dev); - struct max6650_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); + struct device *dev = &client->dev; + struct max6650_data *data; + struct device *hwmon_dev; + int err; - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - data->speed = i2c_smbus_read_byte_data(client, - MAX6650_REG_SPEED); - data->config = i2c_smbus_read_byte_data(client, - MAX6650_REG_CONFIG); - for (i = 0; i < data->nr_fans; i++) { - data->tach[i] = i2c_smbus_read_byte_data(client, - tach_reg[i]); - } - data->count = i2c_smbus_read_byte_data(client, - MAX6650_REG_COUNT); - data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC); + data = devm_kzalloc(dev, sizeof(struct max6650_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - /* - * Alarms are cleared on read in case the condition that - * caused the alarm is removed. Keep the value latched here - * for providing the register through different alarm files. - */ - data->alarm |= i2c_smbus_read_byte_data(client, - MAX6650_REG_ALARM); + data->client = client; + mutex_init(&data->update_lock); + data->nr_fans = id->driver_data; - data->last_updated = jiffies; - data->valid = 1; - } + /* + * Initialize the max6650 chip + */ + err = max6650_init_client(data, client); + if (err) + return err; - mutex_unlock(&data->update_lock); + data->groups[0] = &max6650_group; + /* 3 additional fan inputs for the MAX6651 */ + if (data->nr_fans == 4) + data->groups[1] = &max6651_group; - return data; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, data, + data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } +static const struct i2c_device_id max6650_id[] = { + { "max6650", 1 }, + { "max6651", 4 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max6650_id); + +static struct i2c_driver max6650_driver = { + .driver = { + .name = "max6650", + }, + .probe = max6650_probe, + .id_table = max6650_id, +}; + module_i2c_driver(max6650_driver); MODULE_AUTHOR("Hans J. Koch"); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index de3c152a1d9a..e24ed521051a 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -1,9 +1,9 @@ /* * Hardware monitoring driver for LTC2974, LTC2977, LTC2978, LTC3880, - * and LTC3883 + * LTC3883, and LTM4676 * * Copyright (c) 2011 Ericsson AB. - * Copyright (c) 2013 Guenter Roeck + * Copyright (c) 2013, 2014 Guenter Roeck * * 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 @@ -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/kernel.h> @@ -28,7 +24,7 @@ #include <linux/i2c.h> #include "pmbus.h" -enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883 }; +enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 }; /* Common for all chips */ #define LTC2978_MFR_VOUT_PEAK 0xdd @@ -45,7 +41,7 @@ enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883 }; #define LTC2974_MFR_IOUT_PEAK 0xd7 #define LTC2974_MFR_IOUT_MIN 0xd8 -/* LTC3880 and LTC3883 */ +/* LTC3880, LTC3883, and LTM4676 */ #define LTC3880_MFR_IOUT_PEAK 0xd7 #define LTC3880_MFR_CLEAR_PEAKS 0xe3 #define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 @@ -53,7 +49,8 @@ enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883 }; /* LTC3883 only */ #define LTC3883_MFR_IIN_PEAK 0xe1 -#define LTC2974_ID 0x0212 +#define LTC2974_ID_REV1 0x0212 +#define LTC2974_ID_REV2 0x0213 #define LTC2977_ID 0x0130 #define LTC2978_ID_REV1 0x0121 #define LTC2978_ID_REV2 0x0122 @@ -62,6 +59,8 @@ enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883 }; #define LTC3880_ID_MASK 0xff00 #define LTC3883_ID 0x4300 #define LTC3883_ID_MASK 0xff00 +#define LTM4676_ID 0x4480 /* datasheet claims 0x440X */ +#define LTM4676_ID_MASK 0xfff0 #define LTC2974_NUM_PAGES 4 #define LTC2978_NUM_PAGES 8 @@ -370,6 +369,7 @@ static const struct i2c_device_id ltc2978_id[] = { {"ltc2978", ltc2978}, {"ltc3880", ltc3880}, {"ltc3883", ltc3883}, + {"ltm4676", ltm4676}, {} }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); @@ -394,7 +394,7 @@ static int ltc2978_probe(struct i2c_client *client, if (chip_id < 0) return chip_id; - if (chip_id == LTC2974_ID) { + if (chip_id == LTC2974_ID_REV1 || chip_id == LTC2974_ID_REV2) { data->id = ltc2974; } else if (chip_id == LTC2977_ID) { data->id = ltc2977; @@ -405,6 +405,8 @@ static int ltc2978_probe(struct i2c_client *client, data->id = ltc3880; } else if ((chip_id & LTC3883_ID_MASK) == LTC3883_ID) { data->id = ltc3883; + } else if ((chip_id & LTM4676_ID_MASK) == LTM4676_ID) { + data->id = ltm4676; } else { dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); return -ENODEV; @@ -458,6 +460,7 @@ static int ltc2978_probe(struct i2c_client *client, } break; case ltc3880: + case ltm4676: info->read_word_data = ltc3880_read_word_data; info->pages = LTC3880_NUM_PAGES; info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN @@ -500,5 +503,5 @@ static struct i2c_driver ltc2978_driver = { module_i2c_driver(ltc2978_driver); MODULE_AUTHOR("Guenter Roeck"); -MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, and LTC3883"); +MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, LTC3883, and LTM4676"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/smm665.c b/drivers/hwmon/smm665.c index d9e1b7de78da..4ef5802df6d8 100644 --- a/drivers/hwmon/smm665.c +++ b/drivers/hwmon/smm665.c @@ -222,7 +222,7 @@ static int smm665_read_adc(struct smm665_data *data, int adc) rv = i2c_smbus_read_word_swapped(client, 0); if (rv < 0) { dev_dbg(&client->dev, "Failed to read ADC value: error %d", rv); - return -1; + return rv; } /* * Validate/verify readback adc channel (in bit 11..14). diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index f5ed03164d86..de17c5593d97 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -387,7 +387,7 @@ config I2C_CBUS_GPIO config I2C_CPM tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)" - depends on (CPM1 || CPM2) && OF_I2C + depends on CPM1 || CPM2 help This supports the use of the I2C interface on Freescale processors with CPM1 or CPM2. diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index be7f0a20d634..f3b89a4698b6 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -39,7 +39,9 @@ #include <linux/i2c.h> #include <linux/io.h> #include <linux/dma-mapping.h> +#include <linux/of_address.h> #include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <sysdev/fsl_soc.h> #include <asm/cpm.h> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 2209f28441e9..5c63f0918d12 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -155,6 +155,16 @@ config MCP3422 This driver can also be built as a module. If so, the module will be called mcp3422. +config MEN_Z188_ADC + tristate "MEN 16z188 ADC IP Core support" + depends on MCB + help + Say yes here to enable support for the MEN 16z188 ADC IP-Core on a MCB + carrier. + + This driver can also be built as a module. If so, the module will be + called men_z188_adc. + config NAU7802 tristate "Nuvoton NAU7802 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ba9a10a24cd0..85a4a045f1f0 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o +obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c new file mode 100644 index 000000000000..6989c16aec2b --- /dev/null +++ b/drivers/iio/adc/men_z188_adc.c @@ -0,0 +1,172 @@ +/* + * MEN 16z188 Analog to Digial Converter + * + * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) + * Author: Johannes Thumshirn <johannes.thumshirn@men.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; version 2 of the License. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mcb.h> +#include <linux/io.h> +#include <linux/iio/iio.h> + +#define Z188_ADC_MAX_CHAN 8 +#define Z188_ADC_GAIN 0x0700000 +#define Z188_MODE_VOLTAGE BIT(27) +#define Z188_CFG_AUTO 0x1 +#define Z188_CTRL_REG 0x40 + +#define ADC_DATA(x) (((x) >> 2) & 0x7ffffc) +#define ADC_OVR(x) ((x) & 0x1) + +struct z188_adc { + struct resource *mem; + void __iomem *base; +}; + +#define Z188_ADC_CHANNEL(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +} + +static const struct iio_chan_spec z188_adc_iio_channels[] = { + Z188_ADC_CHANNEL(0), + Z188_ADC_CHANNEL(1), + Z188_ADC_CHANNEL(2), + Z188_ADC_CHANNEL(3), + Z188_ADC_CHANNEL(4), + Z188_ADC_CHANNEL(5), + Z188_ADC_CHANNEL(6), + Z188_ADC_CHANNEL(7), +}; + +static int z188_iio_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long info) +{ + struct z188_adc *adc = iio_priv(iio_dev); + int ret; + u16 tmp; + + switch (info) { + case IIO_CHAN_INFO_RAW: + tmp = readw(adc->base + chan->channel * 4); + + if (ADC_OVR(tmp)) { + dev_info(&iio_dev->dev, + "Oversampling error on ADC channel %d\n", + chan->channel); + return -EIO; + } + *val = ADC_DATA(tmp); + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static struct iio_info z188_adc_info = { + .read_raw = &z188_iio_read_raw, + .driver_module = THIS_MODULE, +}; + +static void men_z188_config_channels(void __iomem *addr) +{ + int i; + u32 cfg; + u32 ctl; + + ctl = readl(addr + Z188_CTRL_REG); + ctl |= Z188_CFG_AUTO; + writel(ctl, addr + Z188_CTRL_REG); + + for (i = 0; i < Z188_ADC_MAX_CHAN; i++) { + cfg = readl(addr + i); + cfg &= ~Z188_ADC_GAIN; + cfg |= Z188_MODE_VOLTAGE; + writel(cfg, addr + i); + } +} + +static int men_z188_probe(struct mcb_device *dev, + const struct mcb_device_id *id) +{ + struct z188_adc *adc; + struct iio_dev *indio_dev; + struct resource *mem; + + indio_dev = devm_iio_device_alloc(&dev->dev, sizeof(struct z188_adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + indio_dev->name = "z188-adc"; + indio_dev->dev.parent = &dev->dev; + indio_dev->info = &z188_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = z188_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(z188_adc_iio_channels); + + mem = mcb_request_mem(dev, "z188-adc"); + if (!mem) + return -ENOMEM; + + adc->base = ioremap(mem->start, resource_size(mem)); + if (adc->base == NULL) + goto err; + + men_z188_config_channels(adc->base); + + adc->mem = mem; + mcb_set_drvdata(dev, indio_dev); + + return iio_device_register(indio_dev); + +err: + mcb_release_mem(mem); + return -ENXIO; +} + +static void men_z188_remove(struct mcb_device *dev) +{ + struct iio_dev *indio_dev = mcb_get_drvdata(dev); + struct z188_adc *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iounmap(adc->base); + mcb_release_mem(adc->mem); +} + +static const struct mcb_device_id men_z188_ids[] = { + { .device = 0xbc }, +}; +MODULE_DEVICE_TABLE(mcb, men_z188_ids); + +static struct mcb_driver men_z188_driver = { + .driver = { + .name = "z188-adc", + .owner = THIS_MODULE, + }, + .probe = men_z188_probe, + .remove = men_z188_remove, + .id_table = men_z188_ids, +}; +module_mcb_driver(men_z188_driver); + +MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core"); +MODULE_ALIAS("mcb:16z188"); diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index e81c5547e647..f9c12e92fdd6 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -53,8 +53,8 @@ #include "user.h" #define DRV_NAME MLX4_IB_DRV_NAME -#define DRV_VERSION "1.0" -#define DRV_RELDATE "April 4, 2008" +#define DRV_VERSION "2.2-1" +#define DRV_RELDATE "Feb 2014" #define MLX4_IB_FLOW_MAX_PRIO 0xFFF #define MLX4_IB_FLOW_QPN_MASK 0xFFFFFF diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index aa03e732b6a8..bf900579ac08 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -46,8 +46,8 @@ #include "mlx5_ib.h" #define DRIVER_NAME "mlx5_ib" -#define DRIVER_VERSION "1.0" -#define DRIVER_RELDATE "June 2013" +#define DRIVER_VERSION "2.2-1" +#define DRIVER_RELDATE "Feb 2014" MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>"); MODULE_DESCRIPTION("Mellanox Connect-IB HCA IB driver"); diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index d18d08a076e8..8ee228e9ab5a 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -492,12 +492,11 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) isert_conn->state = ISER_CONN_INIT; INIT_LIST_HEAD(&isert_conn->conn_accept_node); init_completion(&isert_conn->conn_login_comp); - init_waitqueue_head(&isert_conn->conn_wait); - init_waitqueue_head(&isert_conn->conn_wait_comp_err); + init_completion(&isert_conn->conn_wait); + init_completion(&isert_conn->conn_wait_comp_err); kref_init(&isert_conn->conn_kref); kref_get(&isert_conn->conn_kref); mutex_init(&isert_conn->conn_mutex); - mutex_init(&isert_conn->conn_comp_mutex); spin_lock_init(&isert_conn->conn_lock); cma_id->context = isert_conn; @@ -688,11 +687,11 @@ isert_disconnect_work(struct work_struct *work) pr_debug("isert_disconnect_work(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); mutex_lock(&isert_conn->conn_mutex); - isert_conn->state = ISER_CONN_DOWN; + if (isert_conn->state == ISER_CONN_UP) + isert_conn->state = ISER_CONN_TERMINATING; if (isert_conn->post_recv_buf_count == 0 && atomic_read(&isert_conn->post_send_buf_count) == 0) { - pr_debug("Calling wake_up(&isert_conn->conn_wait);\n"); mutex_unlock(&isert_conn->conn_mutex); goto wake_up; } @@ -712,7 +711,7 @@ isert_disconnect_work(struct work_struct *work) mutex_unlock(&isert_conn->conn_mutex); wake_up: - wake_up(&isert_conn->conn_wait); + complete(&isert_conn->conn_wait); isert_put_conn(isert_conn); } @@ -888,16 +887,17 @@ isert_init_send_wr(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd, * Coalesce send completion interrupts by only setting IB_SEND_SIGNALED * bit for every ISERT_COMP_BATCH_COUNT number of ib_post_send() calls. */ - mutex_lock(&isert_conn->conn_comp_mutex); - if (coalesce && + mutex_lock(&isert_conn->conn_mutex); + if (coalesce && isert_conn->state == ISER_CONN_UP && ++isert_conn->conn_comp_batch < ISERT_COMP_BATCH_COUNT) { + tx_desc->llnode_active = true; llist_add(&tx_desc->comp_llnode, &isert_conn->conn_comp_llist); - mutex_unlock(&isert_conn->conn_comp_mutex); + mutex_unlock(&isert_conn->conn_mutex); return; } isert_conn->conn_comp_batch = 0; tx_desc->comp_llnode_batch = llist_del_all(&isert_conn->conn_comp_llist); - mutex_unlock(&isert_conn->conn_comp_mutex); + mutex_unlock(&isert_conn->conn_mutex); send_wr->send_flags = IB_SEND_SIGNALED; } @@ -1464,7 +1464,7 @@ isert_put_cmd(struct isert_cmd *isert_cmd) case ISCSI_OP_SCSI_CMD: spin_lock_bh(&conn->cmd_lock); if (!list_empty(&cmd->i_conn_node)) - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); if (cmd->data_direction == DMA_TO_DEVICE) @@ -1476,7 +1476,7 @@ isert_put_cmd(struct isert_cmd *isert_cmd) case ISCSI_OP_SCSI_TMFUNC: spin_lock_bh(&conn->cmd_lock); if (!list_empty(&cmd->i_conn_node)) - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); transport_generic_free_cmd(&cmd->se_cmd, 0); @@ -1486,7 +1486,7 @@ isert_put_cmd(struct isert_cmd *isert_cmd) case ISCSI_OP_TEXT: spin_lock_bh(&conn->cmd_lock); if (!list_empty(&cmd->i_conn_node)) - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); /* @@ -1549,6 +1549,7 @@ isert_completion_rdma_read(struct iser_tx_desc *tx_desc, iscsit_stop_dataout_timer(cmd); device->unreg_rdma_mem(isert_cmd, isert_conn); cmd->write_data_done = wr->cur_rdma_length; + wr->send_wr_num = 0; pr_debug("Cmd: %p RDMA_READ comp calling execute_cmd\n", isert_cmd); spin_lock_bh(&cmd->istate_lock); @@ -1589,7 +1590,7 @@ isert_do_control_comp(struct work_struct *work) pr_debug("Calling iscsit_logout_post_handler >>>>>>>>>>>>>>\n"); /* * Call atomic_dec(&isert_conn->post_send_buf_count) - * from isert_free_conn() + * from isert_wait_conn() */ isert_conn->logout_posted = true; iscsit_logout_post_handler(cmd, cmd->conn); @@ -1613,6 +1614,7 @@ isert_response_completion(struct iser_tx_desc *tx_desc, struct ib_device *ib_dev) { struct iscsi_cmd *cmd = isert_cmd->iscsi_cmd; + struct isert_rdma_wr *wr = &isert_cmd->rdma_wr; if (cmd->i_state == ISTATE_SEND_TASKMGTRSP || cmd->i_state == ISTATE_SEND_LOGOUTRSP || @@ -1624,7 +1626,7 @@ isert_response_completion(struct iser_tx_desc *tx_desc, queue_work(isert_comp_wq, &isert_cmd->comp_work); return; } - atomic_dec(&isert_conn->post_send_buf_count); + atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count); cmd->i_state = ISTATE_SENT_STATUS; isert_completion_put(tx_desc, isert_cmd, ib_dev); @@ -1662,7 +1664,7 @@ __isert_send_completion(struct iser_tx_desc *tx_desc, case ISER_IB_RDMA_READ: pr_debug("isert_send_completion: Got ISER_IB_RDMA_READ:\n"); - atomic_dec(&isert_conn->post_send_buf_count); + atomic_sub(wr->send_wr_num, &isert_conn->post_send_buf_count); isert_completion_rdma_read(tx_desc, isert_cmd); break; default: @@ -1691,31 +1693,76 @@ isert_send_completion(struct iser_tx_desc *tx_desc, } static void -isert_cq_comp_err(struct iser_tx_desc *tx_desc, struct isert_conn *isert_conn) +isert_cq_drain_comp_llist(struct isert_conn *isert_conn, struct ib_device *ib_dev) +{ + struct llist_node *llnode; + struct isert_rdma_wr *wr; + struct iser_tx_desc *t; + + mutex_lock(&isert_conn->conn_mutex); + llnode = llist_del_all(&isert_conn->conn_comp_llist); + isert_conn->conn_comp_batch = 0; + mutex_unlock(&isert_conn->conn_mutex); + + while (llnode) { + t = llist_entry(llnode, struct iser_tx_desc, comp_llnode); + llnode = llist_next(llnode); + wr = &t->isert_cmd->rdma_wr; + + atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count); + isert_completion_put(t, t->isert_cmd, ib_dev); + } +} + +static void +isert_cq_tx_comp_err(struct iser_tx_desc *tx_desc, struct isert_conn *isert_conn) { struct ib_device *ib_dev = isert_conn->conn_cm_id->device; + struct isert_cmd *isert_cmd = tx_desc->isert_cmd; + struct llist_node *llnode = tx_desc->comp_llnode_batch; + struct isert_rdma_wr *wr; + struct iser_tx_desc *t; - if (tx_desc) { - struct isert_cmd *isert_cmd = tx_desc->isert_cmd; + while (llnode) { + t = llist_entry(llnode, struct iser_tx_desc, comp_llnode); + llnode = llist_next(llnode); + wr = &t->isert_cmd->rdma_wr; - if (!isert_cmd) - isert_unmap_tx_desc(tx_desc, ib_dev); - else - isert_completion_put(tx_desc, isert_cmd, ib_dev); + atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count); + isert_completion_put(t, t->isert_cmd, ib_dev); } + tx_desc->comp_llnode_batch = NULL; - if (isert_conn->post_recv_buf_count == 0 && - atomic_read(&isert_conn->post_send_buf_count) == 0) { - pr_debug("isert_cq_comp_err >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); - pr_debug("Calling wake_up from isert_cq_comp_err\n"); + if (!isert_cmd) + isert_unmap_tx_desc(tx_desc, ib_dev); + else + isert_completion_put(tx_desc, isert_cmd, ib_dev); +} - mutex_lock(&isert_conn->conn_mutex); - if (isert_conn->state != ISER_CONN_DOWN) - isert_conn->state = ISER_CONN_TERMINATING; - mutex_unlock(&isert_conn->conn_mutex); +static void +isert_cq_rx_comp_err(struct isert_conn *isert_conn) +{ + struct ib_device *ib_dev = isert_conn->conn_cm_id->device; + struct iscsi_conn *conn = isert_conn->conn; - wake_up(&isert_conn->conn_wait_comp_err); + if (isert_conn->post_recv_buf_count) + return; + + isert_cq_drain_comp_llist(isert_conn, ib_dev); + + if (conn->sess) { + target_sess_cmd_list_set_waiting(conn->sess->se_sess); + target_wait_for_sess_cmds(conn->sess->se_sess); } + + while (atomic_read(&isert_conn->post_send_buf_count)) + msleep(3000); + + mutex_lock(&isert_conn->conn_mutex); + isert_conn->state = ISER_CONN_DOWN; + mutex_unlock(&isert_conn->conn_mutex); + + complete(&isert_conn->conn_wait_comp_err); } static void @@ -1740,8 +1787,14 @@ isert_cq_tx_work(struct work_struct *work) pr_debug("TX wc.status != IB_WC_SUCCESS >>>>>>>>>>>>>>\n"); pr_debug("TX wc.status: 0x%08x\n", wc.status); pr_debug("TX wc.vendor_err: 0x%08x\n", wc.vendor_err); - atomic_dec(&isert_conn->post_send_buf_count); - isert_cq_comp_err(tx_desc, isert_conn); + + if (wc.wr_id != ISER_FASTREG_LI_WRID) { + if (tx_desc->llnode_active) + continue; + + atomic_dec(&isert_conn->post_send_buf_count); + isert_cq_tx_comp_err(tx_desc, isert_conn); + } } } @@ -1784,7 +1837,7 @@ isert_cq_rx_work(struct work_struct *work) wc.vendor_err); } isert_conn->post_recv_buf_count--; - isert_cq_comp_err(NULL, isert_conn); + isert_cq_rx_comp_err(isert_conn); } } @@ -2202,6 +2255,7 @@ isert_fast_reg_mr(struct fast_reg_descriptor *fr_desc, if (!fr_desc->valid) { memset(&inv_wr, 0, sizeof(inv_wr)); + inv_wr.wr_id = ISER_FASTREG_LI_WRID; inv_wr.opcode = IB_WR_LOCAL_INV; inv_wr.ex.invalidate_rkey = fr_desc->data_mr->rkey; wr = &inv_wr; @@ -2212,6 +2266,7 @@ isert_fast_reg_mr(struct fast_reg_descriptor *fr_desc, /* Prepare FASTREG WR */ memset(&fr_wr, 0, sizeof(fr_wr)); + fr_wr.wr_id = ISER_FASTREG_LI_WRID; fr_wr.opcode = IB_WR_FAST_REG_MR; fr_wr.wr.fast_reg.iova_start = fr_desc->data_frpl->page_list[0] + page_off; @@ -2377,12 +2432,12 @@ isert_put_datain(struct iscsi_conn *conn, struct iscsi_cmd *cmd) isert_init_send_wr(isert_conn, isert_cmd, &isert_cmd->tx_desc.send_wr, true); - atomic_inc(&isert_conn->post_send_buf_count); + atomic_add(wr->send_wr_num + 1, &isert_conn->post_send_buf_count); rc = ib_post_send(isert_conn->conn_qp, wr->send_wr, &wr_failed); if (rc) { pr_warn("ib_post_send() failed for IB_WR_RDMA_WRITE\n"); - atomic_dec(&isert_conn->post_send_buf_count); + atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count); } pr_debug("Cmd: %p posted RDMA_WRITE + Response for iSER Data READ\n", isert_cmd); @@ -2410,12 +2465,12 @@ isert_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd, bool recovery) return rc; } - atomic_inc(&isert_conn->post_send_buf_count); + atomic_add(wr->send_wr_num, &isert_conn->post_send_buf_count); rc = ib_post_send(isert_conn->conn_qp, wr->send_wr, &wr_failed); if (rc) { pr_warn("ib_post_send() failed for IB_WR_RDMA_READ\n"); - atomic_dec(&isert_conn->post_send_buf_count); + atomic_sub(wr->send_wr_num, &isert_conn->post_send_buf_count); } pr_debug("Cmd: %p posted RDMA_READ memory for ISER Data WRITE\n", isert_cmd); @@ -2702,22 +2757,11 @@ isert_free_np(struct iscsi_np *np) kfree(isert_np); } -static int isert_check_state(struct isert_conn *isert_conn, int state) -{ - int ret; - - mutex_lock(&isert_conn->conn_mutex); - ret = (isert_conn->state == state); - mutex_unlock(&isert_conn->conn_mutex); - - return ret; -} - -static void isert_free_conn(struct iscsi_conn *conn) +static void isert_wait_conn(struct iscsi_conn *conn) { struct isert_conn *isert_conn = conn->context; - pr_debug("isert_free_conn: Starting \n"); + pr_debug("isert_wait_conn: Starting \n"); /* * Decrement post_send_buf_count for special case when called * from isert_do_control_comp() -> iscsit_logout_post_handler() @@ -2727,38 +2771,29 @@ static void isert_free_conn(struct iscsi_conn *conn) atomic_dec(&isert_conn->post_send_buf_count); if (isert_conn->conn_cm_id && isert_conn->state != ISER_CONN_DOWN) { - pr_debug("Calling rdma_disconnect from isert_free_conn\n"); + pr_debug("Calling rdma_disconnect from isert_wait_conn\n"); rdma_disconnect(isert_conn->conn_cm_id); } /* * Only wait for conn_wait_comp_err if the isert_conn made it * into full feature phase.. */ - if (isert_conn->state == ISER_CONN_UP) { - pr_debug("isert_free_conn: Before wait_event comp_err %d\n", - isert_conn->state); - mutex_unlock(&isert_conn->conn_mutex); - - wait_event(isert_conn->conn_wait_comp_err, - (isert_check_state(isert_conn, ISER_CONN_TERMINATING))); - - wait_event(isert_conn->conn_wait, - (isert_check_state(isert_conn, ISER_CONN_DOWN))); - - isert_put_conn(isert_conn); - return; - } if (isert_conn->state == ISER_CONN_INIT) { mutex_unlock(&isert_conn->conn_mutex); - isert_put_conn(isert_conn); return; } - pr_debug("isert_free_conn: wait_event conn_wait %d\n", - isert_conn->state); + if (isert_conn->state == ISER_CONN_UP) + isert_conn->state = ISER_CONN_TERMINATING; mutex_unlock(&isert_conn->conn_mutex); - wait_event(isert_conn->conn_wait, - (isert_check_state(isert_conn, ISER_CONN_DOWN))); + wait_for_completion(&isert_conn->conn_wait_comp_err); + + wait_for_completion(&isert_conn->conn_wait); +} + +static void isert_free_conn(struct iscsi_conn *conn) +{ + struct isert_conn *isert_conn = conn->context; isert_put_conn(isert_conn); } @@ -2771,6 +2806,7 @@ static struct iscsit_transport iser_target_transport = { .iscsit_setup_np = isert_setup_np, .iscsit_accept_np = isert_accept_np, .iscsit_free_np = isert_free_np, + .iscsit_wait_conn = isert_wait_conn, .iscsit_free_conn = isert_free_conn, .iscsit_get_login_rx = isert_get_login_rx, .iscsit_put_login_tx = isert_put_login_tx, diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h index 708a069002f3..f6ae7f5dd408 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h @@ -6,6 +6,7 @@ #define ISERT_RDMA_LISTEN_BACKLOG 10 #define ISCSI_ISER_SG_TABLESIZE 256 +#define ISER_FASTREG_LI_WRID 0xffffffffffffffffULL enum isert_desc_type { ISCSI_TX_CONTROL, @@ -45,6 +46,7 @@ struct iser_tx_desc { struct isert_cmd *isert_cmd; struct llist_node *comp_llnode_batch; struct llist_node comp_llnode; + bool llnode_active; struct ib_send_wr send_wr; } __packed; @@ -116,8 +118,8 @@ struct isert_conn { struct isert_device *conn_device; struct work_struct conn_logout_work; struct mutex conn_mutex; - wait_queue_head_t conn_wait; - wait_queue_head_t conn_wait_comp_err; + struct completion conn_wait; + struct completion conn_wait_comp_err; struct kref conn_kref; struct list_head conn_fr_pool; int conn_fr_pool_size; @@ -126,7 +128,6 @@ struct isert_conn { #define ISERT_COMP_BATCH_COUNT 8 int conn_comp_batch; struct llist_head conn_comp_llist; - struct mutex conn_comp_mutex; }; #define ISERT_MAX_CQ 64 diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index a06e12552886..ce953d895f5b 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -954,11 +954,13 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return -EFAULT; error = input_ff_upload(dev, &effect, file); + if (error) + return error; if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) return -EFAULT; - return error; + return 0; } /* Multi-number variable-length handlers */ diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index bb3b57bea8ba..5ef7fcf0e250 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -76,8 +76,18 @@ static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); unsigned int bank = ADP5588_BANK(kpad->gpiomap[off]); unsigned int bit = ADP5588_BIT(kpad->gpiomap[off]); + int val; - return !!(adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank) & bit); + mutex_lock(&kpad->gpio_lock); + + if (kpad->dir[bank] & bit) + val = kpad->dat_out[bank]; + else + val = adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank); + + mutex_unlock(&kpad->gpio_lock); + + return !!(val & bit); } static void adp5588_gpio_set_value(struct gpio_chip *chip, diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c index 1f695f229ea8..184c8f21ab59 100644 --- a/drivers/input/misc/da9052_onkey.c +++ b/drivers/input/misc/da9052_onkey.c @@ -27,29 +27,32 @@ struct da9052_onkey { static void da9052_onkey_query(struct da9052_onkey *onkey) { - int key_stat; + int ret; - key_stat = da9052_reg_read(onkey->da9052, DA9052_EVENT_B_REG); - if (key_stat < 0) { + ret = da9052_reg_read(onkey->da9052, DA9052_STATUS_A_REG); + if (ret < 0) { dev_err(onkey->da9052->dev, - "Failed to read onkey event %d\n", key_stat); + "Failed to read onkey event err=%d\n", ret); } else { /* * Since interrupt for deassertion of ONKEY pin is not * generated, onkey event state determines the onkey * button state. */ - key_stat &= DA9052_EVENTB_ENONKEY; - input_report_key(onkey->input, KEY_POWER, key_stat); + bool pressed = !(ret & DA9052_STATUSA_NONKEY); + + input_report_key(onkey->input, KEY_POWER, pressed); input_sync(onkey->input); - } - /* - * Interrupt is generated only when the ONKEY pin is asserted. - * Hence the deassertion of the pin is simulated through work queue. - */ - if (key_stat) - schedule_delayed_work(&onkey->work, msecs_to_jiffies(50)); + /* + * Interrupt is generated only when the ONKEY pin + * is asserted. Hence the deassertion of the pin + * is simulated through work queue. + */ + if (pressed) + schedule_delayed_work(&onkey->work, + msecs_to_jiffies(50)); + } } static void da9052_onkey_work(struct work_struct *work) diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c index 87095e2f5153..8af34ffe208b 100644 --- a/drivers/input/mouse/cypress_ps2.c +++ b/drivers/input/mouse/cypress_ps2.c @@ -409,7 +409,6 @@ static int cypress_set_input_params(struct input_dev *input, __clear_bit(REL_X, input->relbit); __clear_bit(REL_Y, input->relbit); - __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); __set_bit(EV_KEY, input->evbit); __set_bit(BTN_LEFT, input->keybit); __set_bit(BTN_RIGHT, input->keybit); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 26386f9d2569..d8d49d10f9bb 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -265,11 +265,22 @@ static int synaptics_identify(struct psmouse *psmouse) * Read touchpad resolution and maximum reported coordinates * Resolution is left zero if touchpad does not support the query */ + +static const int *quirk_min_max; + static int synaptics_resolution(struct psmouse *psmouse) { struct synaptics_data *priv = psmouse->private; unsigned char resp[3]; + if (quirk_min_max) { + priv->x_min = quirk_min_max[0]; + priv->x_max = quirk_min_max[1]; + priv->y_min = quirk_min_max[2]; + priv->y_max = quirk_min_max[3]; + return 0; + } + if (SYN_ID_MAJOR(priv->identity) < 4) return 0; @@ -1485,10 +1496,54 @@ static const struct dmi_system_id olpc_dmi_table[] __initconst = { { } }; +static const struct dmi_system_id min_max_dmi_table[] __initconst = { +#if defined(CONFIG_DMI) + { + /* Lenovo ThinkPad Helix */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix"), + }, + .driver_data = (int []){1024, 5052, 2258, 4832}, + }, + { + /* Lenovo ThinkPad X240 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X240"), + }, + .driver_data = (int []){1232, 5710, 1156, 4696}, + }, + { + /* Lenovo ThinkPad T440s */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T440"), + }, + .driver_data = (int []){1024, 5112, 2024, 4832}, + }, + { + /* Lenovo ThinkPad T540p */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T540"), + }, + .driver_data = (int []){1024, 5056, 2058, 4832}, + }, +#endif + { } +}; + void __init synaptics_module_init(void) { + const struct dmi_system_id *min_max_dmi; + impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); broken_olpc_ec = dmi_check_system(olpc_dmi_table); + + min_max_dmi = dmi_first_match(min_max_dmi_table); + if (min_max_dmi) + quirk_min_max = min_max_dmi->driver_data; } static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 4c842c320c2e..b604564dec5c 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -67,7 +67,6 @@ struct mousedev { struct device dev; struct cdev cdev; bool exist; - bool is_mixdev; struct list_head mixdev_node; bool opened_by_mixdev; @@ -77,6 +76,9 @@ struct mousedev { int old_x[4], old_y[4]; int frac_dx, frac_dy; unsigned long touch; + + int (*open_device)(struct mousedev *mousedev); + void (*close_device)(struct mousedev *mousedev); }; enum mousedev_emul { @@ -116,9 +118,6 @@ static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; static struct mousedev *mousedev_mix; static LIST_HEAD(mousedev_mix_list); -static void mixdev_open_devices(void); -static void mixdev_close_devices(void); - #define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03]) #define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03]) @@ -428,9 +427,7 @@ static int mousedev_open_device(struct mousedev *mousedev) if (retval) return retval; - if (mousedev->is_mixdev) - mixdev_open_devices(); - else if (!mousedev->exist) + if (!mousedev->exist) retval = -ENODEV; else if (!mousedev->open++) { retval = input_open_device(&mousedev->handle); @@ -446,9 +443,7 @@ static void mousedev_close_device(struct mousedev *mousedev) { mutex_lock(&mousedev->mutex); - if (mousedev->is_mixdev) - mixdev_close_devices(); - else if (mousedev->exist && !--mousedev->open) + if (mousedev->exist && !--mousedev->open) input_close_device(&mousedev->handle); mutex_unlock(&mousedev->mutex); @@ -459,21 +454,29 @@ static void mousedev_close_device(struct mousedev *mousedev) * stream. Note that this function is called with mousedev_mix->mutex * held. */ -static void mixdev_open_devices(void) +static int mixdev_open_devices(struct mousedev *mixdev) { - struct mousedev *mousedev; + int error; + + error = mutex_lock_interruptible(&mixdev->mutex); + if (error) + return error; - if (mousedev_mix->open++) - return; + if (!mixdev->open++) { + struct mousedev *mousedev; - list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { - if (!mousedev->opened_by_mixdev) { - if (mousedev_open_device(mousedev)) - continue; + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { + if (!mousedev->opened_by_mixdev) { + if (mousedev_open_device(mousedev)) + continue; - mousedev->opened_by_mixdev = true; + mousedev->opened_by_mixdev = true; + } } } + + mutex_unlock(&mixdev->mutex); + return 0; } /* @@ -481,19 +484,22 @@ static void mixdev_open_devices(void) * device. Note that this function is called with mousedev_mix->mutex * held. */ -static void mixdev_close_devices(void) +static void mixdev_close_devices(struct mousedev *mixdev) { - struct mousedev *mousedev; + mutex_lock(&mixdev->mutex); - if (--mousedev_mix->open) - return; + if (!--mixdev->open) { + struct mousedev *mousedev; - list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { - if (mousedev->opened_by_mixdev) { - mousedev->opened_by_mixdev = false; - mousedev_close_device(mousedev); + list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { + if (mousedev->opened_by_mixdev) { + mousedev->opened_by_mixdev = false; + mousedev_close_device(mousedev); + } } } + + mutex_unlock(&mixdev->mutex); } @@ -522,7 +528,7 @@ static int mousedev_release(struct inode *inode, struct file *file) mousedev_detach_client(mousedev, client); kfree(client); - mousedev_close_device(mousedev); + mousedev->close_device(mousedev); return 0; } @@ -550,7 +556,7 @@ static int mousedev_open(struct inode *inode, struct file *file) client->mousedev = mousedev; mousedev_attach_client(mousedev, client); - error = mousedev_open_device(mousedev); + error = mousedev->open_device(mousedev); if (error) goto err_free_client; @@ -861,16 +867,21 @@ static struct mousedev *mousedev_create(struct input_dev *dev, if (mixdev) { dev_set_name(&mousedev->dev, "mice"); + + mousedev->open_device = mixdev_open_devices; + mousedev->close_device = mixdev_close_devices; } else { int dev_no = minor; /* Normalize device number if it falls into legacy range */ if (dev_no < MOUSEDEV_MINOR_BASE + MOUSEDEV_MINORS) dev_no -= MOUSEDEV_MINOR_BASE; dev_set_name(&mousedev->dev, "mouse%d", dev_no); + + mousedev->open_device = mousedev_open_device; + mousedev->close_device = mousedev_close_device; } mousedev->exist = true; - mousedev->is_mixdev = mixdev; mousedev->handle.dev = input_get_device(dev); mousedev->handle.name = dev_name(&mousedev->dev); mousedev->handle.handler = handler; @@ -919,7 +930,7 @@ static void mousedev_destroy(struct mousedev *mousedev) device_del(&mousedev->dev); mousedev_cleanup(mousedev); input_free_minor(MINOR(mousedev->dev.devt)); - if (!mousedev->is_mixdev) + if (mousedev != mousedev_mix) input_unregister_handle(&mousedev->handle); put_device(&mousedev->dev); } diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 5c342b3139e8..3c0f57efe7b1 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -134,7 +134,8 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) } else if (!ts->low_latency_req.dev) { /* First contact, request 100 us latency. */ dev_pm_qos_add_ancestor_request(&ts->client->dev, - &ts->low_latency_req, 100); + &ts->low_latency_req, + DEV_PM_QOS_RESUME_LATENCY, 100); } /* SYN_REPORT */ diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index e400fbe411de..cff039df056e 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -25,6 +25,7 @@ #include <linux/list.h> #include <linux/spinlock.h> #include <linux/pci.h> +#include <linux/irqreturn.h> /* * Maximum number of IOMMUs supported diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 5194afb39e78..1c0c151d108c 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o +obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o obj-$(CONFIG_ARM_NVIC) += irq-nvic.o diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 540956465ed2..41be897df8d5 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/irqchip/chained_irq.h> #include <linux/io.h> #include <linux/of_address.h> #include <linux/of_irq.h> @@ -42,6 +43,7 @@ #define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4) #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) +#define ARMADA_375_PPI_CAUSE (0x10) #define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4) #define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc) @@ -352,7 +354,63 @@ static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { .xlate = irq_domain_xlate_onecell, }; -static asmlinkage void __exception_irq_entry +#ifdef CONFIG_PCI_MSI +static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) +{ + u32 msimask, msinr; + + msimask = readl_relaxed(per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) + & PCI_MSI_DOORBELL_MASK; + + writel(~msimask, per_cpu_int_base + + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); + + for (msinr = PCI_MSI_DOORBELL_START; + msinr < PCI_MSI_DOORBELL_END; msinr++) { + int irq; + + if (!(msimask & BIT(msinr))) + continue; + + irq = irq_find_mapping(armada_370_xp_msi_domain, + msinr - 16); + + if (is_chained) + generic_handle_irq(irq); + else + handle_IRQ(irq, regs); + } +} +#else +static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {} +#endif + +static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq, + struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + unsigned long irqmap, irqn; + unsigned int cascade_irq; + + chained_irq_enter(chip, desc); + + irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE); + + if (irqmap & BIT(0)) { + armada_370_xp_handle_msi_irq(NULL, true); + irqmap &= ~BIT(0); + } + + for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) { + cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn); + generic_handle_irq(cascade_irq); + } + + chained_irq_exit(chip, desc); +} + +static void __exception_irq_entry armada_370_xp_handle_irq(struct pt_regs *regs) { u32 irqstat, irqnr; @@ -372,31 +430,9 @@ armada_370_xp_handle_irq(struct pt_regs *regs) continue; } -#ifdef CONFIG_PCI_MSI /* MSI handling */ - if (irqnr == 1) { - u32 msimask, msinr; - - msimask = readl_relaxed(per_cpu_int_base + - ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) - & PCI_MSI_DOORBELL_MASK; - - writel(~msimask, per_cpu_int_base + - ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); - - for (msinr = PCI_MSI_DOORBELL_START; - msinr < PCI_MSI_DOORBELL_END; msinr++) { - int irq; - - if (!(msimask & BIT(msinr))) - continue; - - irq = irq_find_mapping(armada_370_xp_msi_domain, - msinr - 16); - handle_IRQ(irq, regs); - } - } -#endif + if (irqnr == 1) + armada_370_xp_handle_msi_irq(regs, false); #ifdef CONFIG_SMP /* IPI Handling */ @@ -427,6 +463,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, struct device_node *parent) { struct resource main_int_res, per_cpu_int_res; + int parent_irq; u32 control; BUG_ON(of_address_to_resource(node, 0, &main_int_res)); @@ -455,8 +492,6 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, BUG_ON(!armada_370_xp_mpic_domain); - irq_set_default_host(armada_370_xp_mpic_domain); - #ifdef CONFIG_SMP armada_xp_mpic_smp_cpu_init(); @@ -472,7 +507,14 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, armada_370_xp_msi_init(node, main_int_res.start); - set_handle_irq(armada_370_xp_handle_irq); + parent_irq = irq_of_parse_and_map(node, 0); + if (parent_irq <= 0) { + irq_set_default_host(armada_370_xp_mpic_domain); + set_handle_irq(armada_370_xp_handle_irq); + } else { + irq_set_chained_handler(parent_irq, + armada_370_xp_mpic_handle_cascade_irq); + } return 0; } diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c index 1693b8e7f26a..5916d6cdafa1 100644 --- a/drivers/irqchip/irq-bcm2835.c +++ b/drivers/irqchip/irq-bcm2835.c @@ -95,7 +95,7 @@ struct armctrl_ic { }; static struct armctrl_ic intc __read_mostly; -static asmlinkage void __exception_irq_entry bcm2835_handle_irq( +static void __exception_irq_entry bcm2835_handle_irq( struct pt_regs *regs); static void armctrl_mask_irq(struct irq_data *d) @@ -196,7 +196,7 @@ static void armctrl_handle_shortcut(int bank, struct pt_regs *regs, handle_IRQ(irq_linear_revmap(intc.domain, irq), regs); } -static asmlinkage void __exception_irq_entry bcm2835_handle_irq( +static void __exception_irq_entry bcm2835_handle_irq( struct pt_regs *regs) { u32 stat, irq; diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 341c6016812d..531769b2433a 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -50,7 +50,7 @@ union gic_base { void __iomem *common_base; - void __percpu __iomem **percpu_base; + void __percpu * __iomem *percpu_base; }; struct gic_chip_data { @@ -279,7 +279,7 @@ static int gic_set_wake(struct irq_data *d, unsigned int on) #define gic_set_wake NULL #endif -static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) { u32 irqstat, irqnr; struct gic_chip_data *gic = &gic_data[0]; @@ -648,7 +648,7 @@ static void __init gic_pm_init(struct gic_chip_data *gic) #endif #ifdef CONFIG_SMP -void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) +static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) { int cpu; unsigned long flags, map = 0; @@ -869,7 +869,7 @@ static struct notifier_block gic_cpu_notifier = { }; #endif -const struct irq_domain_ops gic_irq_domain_ops = { +static const struct irq_domain_ops gic_irq_domain_ops = { .map = gic_irq_domain_map, .xlate = gic_irq_domain_xlate, }; @@ -974,7 +974,8 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, #ifdef CONFIG_OF static int gic_cnt __initdata; -int __init gic_of_init(struct device_node *node, struct device_node *parent) +static int __init +gic_of_init(struct device_node *node, struct device_node *parent) { void __iomem *cpu_base; void __iomem *dist_base; diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c index 2cb7cd0bc2f5..3c8827fe83f3 100644 --- a/drivers/irqchip/irq-mmp.c +++ b/drivers/irqchip/irq-mmp.c @@ -194,8 +194,7 @@ static struct mmp_intc_conf mmp2_conf = { .conf_mask = 0x7f, }; -static asmlinkage void __exception_irq_entry -mmp_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry mmp_handle_irq(struct pt_regs *regs) { int irq, hwirq; @@ -207,8 +206,7 @@ mmp_handle_irq(struct pt_regs *regs) handle_IRQ(irq, regs); } -static asmlinkage void __exception_irq_entry -mmp2_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry mmp2_handle_irq(struct pt_regs *regs) { int irq, hwirq; diff --git a/drivers/irqchip/irq-moxart.c b/drivers/irqchip/irq-moxart.c index 5552fc2bf28a..00b3cc908f76 100644 --- a/drivers/irqchip/irq-moxart.c +++ b/drivers/irqchip/irq-moxart.c @@ -44,7 +44,7 @@ struct moxart_irq_data { static struct moxart_irq_data intc; -static asmlinkage void __exception_irq_entry handle_irq(struct pt_regs *regs) +static void __exception_irq_entry handle_irq(struct pt_regs *regs) { u32 irqstat; int hwirq; diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c index 8e41be62812e..e25f246cd2fb 100644 --- a/drivers/irqchip/irq-orion.c +++ b/drivers/irqchip/irq-orion.c @@ -30,7 +30,7 @@ static struct irq_domain *orion_irq_domain; -static asmlinkage void +static void __exception_irq_entry orion_handle_irq(struct pt_regs *regs) { struct irq_domain_chip_generic *dgc = orion_irq_domain->gc; diff --git a/drivers/irqchip/irq-sirfsoc.c b/drivers/irqchip/irq-sirfsoc.c index 3a070c587ed9..581eefe331ae 100644 --- a/drivers/irqchip/irq-sirfsoc.c +++ b/drivers/irqchip/irq-sirfsoc.c @@ -47,7 +47,7 @@ sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) ct->regs.mask = SIRFSOC_INT_RISC_MASK0; } -static asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs) { void __iomem *base = sirfsoc_irqdomain->host_data; u32 irqstat, irqnr; diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index a5438d889245..6fcef4a95a18 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -36,18 +36,16 @@ static void __iomem *sun4i_irq_base; static struct irq_domain *sun4i_irq_domain; -static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs); +static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs); static void sun4i_irq_ack(struct irq_data *irqd) { unsigned int irq = irqd_to_hwirq(irqd); - unsigned int irq_off = irq % 32; - int reg = irq / 32; - u32 val; - val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); - writel(val | (1 << irq_off), - sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); + if (irq != 0) + return; /* Only IRQ 0 / the ENMI needs to be acked */ + + writel(BIT(0), sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)); } static void sun4i_irq_mask(struct irq_data *irqd) @@ -76,16 +74,16 @@ static void sun4i_irq_unmask(struct irq_data *irqd) static struct irq_chip sun4i_irq_chip = { .name = "sun4i_irq", - .irq_ack = sun4i_irq_ack, + .irq_eoi = sun4i_irq_ack, .irq_mask = sun4i_irq_mask, .irq_unmask = sun4i_irq_unmask, + .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED, }; static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) { - irq_set_chip_and_handler(virq, &sun4i_irq_chip, - handle_level_irq); + irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq); set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); return 0; @@ -109,7 +107,7 @@ static int __init sun4i_of_init(struct device_node *node, writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1)); writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2)); - /* Mask all the interrupts */ + /* Unmask all the interrupts, ENABLE_REG(x) is used for masking */ writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0)); writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1)); writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2)); @@ -134,16 +132,30 @@ static int __init sun4i_of_init(struct device_node *node, return 0; } -IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-ic", sun4i_of_init); +IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_of_init); -static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) { u32 irq, hwirq; + /* + * hwirq == 0 can mean one of 3 things: + * 1) no more irqs pending + * 2) irq 0 pending + * 3) spurious irq + * So if we immediately get a reading of 0, check the irq-pending reg + * to differentiate between 2 and 3. We only do this once to avoid + * the extra check in the common case of 1 hapening after having + * read the vector-reg once. + */ hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; - while (hwirq != 0) { + if (hwirq == 0 && + !(readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)) & BIT(0))) + return; + + do { irq = irq_find_mapping(sun4i_irq_domain, hwirq); handle_IRQ(irq, regs); hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; - } + } while (hwirq != 0); } diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c new file mode 100644 index 000000000000..12f547a44ae4 --- /dev/null +++ b/drivers/irqchip/irq-sunxi-nmi.c @@ -0,0 +1,208 @@ +/* + * Allwinner A20/A31 SoCs NMI IRQ chip driver. + * + * Carlo Caione <carlo.caione@gmail.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/bitops.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/irqdomain.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/irqchip/chained_irq.h> +#include "irqchip.h" + +#define SUNXI_NMI_SRC_TYPE_MASK 0x00000003 + +enum { + SUNXI_SRC_TYPE_LEVEL_LOW = 0, + SUNXI_SRC_TYPE_EDGE_FALLING, + SUNXI_SRC_TYPE_LEVEL_HIGH, + SUNXI_SRC_TYPE_EDGE_RISING, +}; + +struct sunxi_sc_nmi_reg_offs { + u32 ctrl; + u32 pend; + u32 enable; +}; + +static struct sunxi_sc_nmi_reg_offs sun7i_reg_offs = { + .ctrl = 0x00, + .pend = 0x04, + .enable = 0x08, +}; + +static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = { + .ctrl = 0x00, + .pend = 0x04, + .enable = 0x34, +}; + +static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off, + u32 val) +{ + irq_reg_writel(val, gc->reg_base + off); +} + +static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off) +{ + return irq_reg_readl(gc->reg_base + off); +} + +static void sunxi_sc_nmi_handle_irq(unsigned int irq, struct irq_desc *desc) +{ + struct irq_domain *domain = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_get_chip(irq); + unsigned int virq = irq_find_mapping(domain, 0); + + chained_irq_enter(chip, desc); + generic_handle_irq(virq); + chained_irq_exit(chip, desc); +} + +static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); + struct irq_chip_type *ct = gc->chip_types; + u32 src_type_reg; + u32 ctrl_off = ct->regs.type; + unsigned int src_type; + unsigned int i; + + irq_gc_lock(gc); + + switch (flow_type & IRQF_TRIGGER_MASK) { + case IRQ_TYPE_EDGE_FALLING: + src_type = SUNXI_SRC_TYPE_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_RISING: + src_type = SUNXI_SRC_TYPE_EDGE_RISING; + break; + case IRQ_TYPE_LEVEL_HIGH: + src_type = SUNXI_SRC_TYPE_LEVEL_HIGH; + break; + case IRQ_TYPE_NONE: + case IRQ_TYPE_LEVEL_LOW: + src_type = SUNXI_SRC_TYPE_LEVEL_LOW; + break; + default: + irq_gc_unlock(gc); + pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n", + __func__, data->irq); + return -EBADR; + } + + irqd_set_trigger_type(data, flow_type); + irq_setup_alt_chip(data, flow_type); + + for (i = 0; i <= gc->num_ct; i++, ct++) + if (ct->type & flow_type) + ctrl_off = ct->regs.type; + + src_type_reg = sunxi_sc_nmi_read(gc, ctrl_off); + src_type_reg &= ~SUNXI_NMI_SRC_TYPE_MASK; + src_type_reg |= src_type; + sunxi_sc_nmi_write(gc, ctrl_off, src_type_reg); + + irq_gc_unlock(gc); + + return IRQ_SET_MASK_OK; +} + +static int __init sunxi_sc_nmi_irq_init(struct device_node *node, + struct sunxi_sc_nmi_reg_offs *reg_offs) +{ + struct irq_domain *domain; + struct irq_chip_generic *gc; + unsigned int irq; + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + int ret; + + + domain = irq_domain_add_linear(node, 1, &irq_generic_chip_ops, NULL); + if (!domain) { + pr_err("%s: Could not register interrupt domain.\n", node->name); + return -ENOMEM; + } + + ret = irq_alloc_domain_generic_chips(domain, 1, 2, node->name, + handle_fasteoi_irq, clr, 0, + IRQ_GC_INIT_MASK_CACHE); + if (ret) { + pr_err("%s: Could not allocate generic interrupt chip.\n", + node->name); + goto fail_irqd_remove; + } + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) { + pr_err("%s: unable to parse irq\n", node->name); + ret = -EINVAL; + goto fail_irqd_remove; + } + + gc = irq_get_domain_generic_chip(domain, 0); + gc->reg_base = of_iomap(node, 0); + if (!gc->reg_base) { + pr_err("%s: unable to map resource\n", node->name); + ret = -ENOMEM; + goto fail_irqd_remove; + } + + gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK; + 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_eoi = irq_gc_ack_set_bit; + gc->chip_types[0].chip.irq_set_type = sunxi_sc_nmi_set_type; + gc->chip_types[0].chip.flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED; + gc->chip_types[0].regs.ack = reg_offs->pend; + gc->chip_types[0].regs.mask = reg_offs->enable; + gc->chip_types[0].regs.type = reg_offs->ctrl; + + gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH; + gc->chip_types[1].chip.name = gc->chip_types[0].chip.name; + gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit; + gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit; + gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit; + gc->chip_types[1].chip.irq_set_type = sunxi_sc_nmi_set_type; + gc->chip_types[1].regs.ack = reg_offs->pend; + gc->chip_types[1].regs.mask = reg_offs->enable; + gc->chip_types[1].regs.type = reg_offs->ctrl; + gc->chip_types[1].handler = handle_edge_irq; + + sunxi_sc_nmi_write(gc, reg_offs->enable, 0); + sunxi_sc_nmi_write(gc, reg_offs->pend, 0x1); + + irq_set_handler_data(irq, domain); + irq_set_chained_handler(irq, sunxi_sc_nmi_handle_irq); + + return 0; + +fail_irqd_remove: + irq_domain_remove(domain); + + return ret; +} + +static int __init sun6i_sc_nmi_irq_init(struct device_node *node, + struct device_node *parent) +{ + return sunxi_sc_nmi_irq_init(node, &sun6i_reg_offs); +} +IRQCHIP_DECLARE(sun6i_sc_nmi, "allwinner,sun6i-a31-sc-nmi", sun6i_sc_nmi_irq_init); + +static int __init sun7i_sc_nmi_irq_init(struct device_node *node, + struct device_node *parent) +{ + return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs); +} +IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init); diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index 8e21ae0bab46..473f09a74d4d 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c @@ -228,7 +228,7 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs) * Keep iterating over all registered VIC's until there are no pending * interrupts. */ -static asmlinkage void __exception_irq_entry vic_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry vic_handle_irq(struct pt_regs *regs) { int i, handled; diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index 1846e7d66681..eb6e91efdec8 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -178,8 +178,7 @@ static struct irq_domain_ops vt8500_irq_domain_ops = { .xlate = irq_domain_xlate_onecell, }; -static asmlinkage -void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) { u32 stat, i; int irqnr, virq; diff --git a/drivers/irqchip/irq-xtensa-mx.c b/drivers/irqchip/irq-xtensa-mx.c index f693f1bc1348..e1c2f9632893 100644 --- a/drivers/irqchip/irq-xtensa-mx.c +++ b/drivers/irqchip/irq-xtensa-mx.c @@ -122,7 +122,7 @@ static int xtensa_mx_irq_retrigger(struct irq_data *d) static int xtensa_mx_irq_set_affinity(struct irq_data *d, const struct cpumask *dest, bool force) { - unsigned mask = 1u << cpumask_any(dest); + unsigned mask = 1u << cpumask_any_and(dest, cpu_online_mask); set_er(mask, MIROUT(d->hwirq - HW_IRQ_MX_BASE)); return 0; diff --git a/drivers/irqchip/irq-zevio.c b/drivers/irqchip/irq-zevio.c index 8ed04c4a43ee..ceb3a4318f73 100644 --- a/drivers/irqchip/irq-zevio.c +++ b/drivers/irqchip/irq-zevio.c @@ -50,7 +50,7 @@ static void zevio_irq_ack(struct irq_data *irqd) readl(gc->reg_base + regs->ack); } -static asmlinkage void __exception_irq_entry zevio_handle_irq(struct pt_regs *regs) +static void __exception_irq_entry zevio_handle_irq(struct pt_regs *regs) { int irqnr; diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c index f496afce29de..cad3e2495552 100644 --- a/drivers/irqchip/irqchip.c +++ b/drivers/irqchip/irqchip.c @@ -10,8 +10,7 @@ #include <linux/init.h> #include <linux/of_irq.h> - -#include "irqchip.h" +#include <linux/irqchip.h> /* * This special of_device_id is the sentinel at the end of the diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig index f04686580040..9816c51eb5c2 100644 --- a/drivers/isdn/capi/Kconfig +++ b/drivers/isdn/capi/Kconfig @@ -16,9 +16,17 @@ config CAPI_TRACE This will increase the size of the kernelcapi module by 20 KB. If unsure, say Y. +config ISDN_CAPI_CAPI20 + tristate "CAPI2.0 /dev/capi support" + help + This option will provide the CAPI 2.0 interface to userspace + applications via /dev/capi20. Applications should use the + standardized libcapi20 to access this functionality. You should say + Y/M here. + config ISDN_CAPI_MIDDLEWARE bool "CAPI2.0 Middleware support" - depends on TTY + depends on ISDN_CAPI_CAPI20 && TTY help This option will enhance the capabilities of the /dev/capi20 interface. It will provide a means of moving a data connection, @@ -26,14 +34,6 @@ config ISDN_CAPI_MIDDLEWARE device. If you want to use pppd with pppdcapiplugin to dial up to your ISP, say Y here. -config ISDN_CAPI_CAPI20 - tristate "CAPI2.0 /dev/capi support" - help - This option will provide the CAPI 2.0 interface to userspace - applications via /dev/capi20. Applications should use the - standardized libcapi20 to access this functionality. You should say - Y/M here. - config ISDN_CAPI_CAPIDRV tristate "CAPI2.0 capidrv interface support" depends on ISDN_I4L diff --git a/drivers/mcb/Kconfig b/drivers/mcb/Kconfig new file mode 100644 index 000000000000..e9a6976e1010 --- /dev/null +++ b/drivers/mcb/Kconfig @@ -0,0 +1,31 @@ +# +# MEN Chameleon Bus (MCB) support +# + +menuconfig MCB + tristate "MCB support" + default n + depends on HAS_IOMEM + help + + The MCB (MEN Chameleon Bus) is a Bus specific to MEN Mikroelektronik + FPGA based devices. It is used to identify MCB based IP-Cores within + an FPGA and provide the necessary framework for instantiating drivers + for these devices. + + If build as a module, the module is called mcb.ko + +if MCB +config MCB_PCI + tristate "PCI based MCB carrier" + default n + depends on PCI + help + + This is a MCB carrier on a PCI device. Both PCI attached on-board + FPGAs as well as CompactPCI attached MCB FPGAs are supported with + this driver. + + If build as a module, the module is called mcb-pci.ko + +endif # MCB diff --git a/drivers/mcb/Makefile b/drivers/mcb/Makefile new file mode 100644 index 000000000000..1ae141311def --- /dev/null +++ b/drivers/mcb/Makefile @@ -0,0 +1,7 @@ + +obj-$(CONFIG_MCB) += mcb.o + +mcb-y += mcb-core.o +mcb-y += mcb-parse.o + +obj-$(CONFIG_MCB_PCI) += mcb-pci.o diff --git a/drivers/mcb/mcb-core.c b/drivers/mcb/mcb-core.c new file mode 100644 index 000000000000..bbe12932d404 --- /dev/null +++ b/drivers/mcb/mcb-core.c @@ -0,0 +1,414 @@ +/* + * MEN Chameleon Bus. + * + * Copyright (C) 2013 MEN Mikroelektronik GmbH (www.men.de) + * Author: Johannes Thumshirn <johannes.thumshirn@men.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; version 2 of the License. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/idr.h> +#include <linux/mcb.h> + +static DEFINE_IDA(mcb_ida); + +static const struct mcb_device_id *mcb_match_id(const struct mcb_device_id *ids, + struct mcb_device *dev) +{ + if (ids) { + while (ids->device) { + if (ids->device == dev->id) + return ids; + ids++; + } + } + + return NULL; +} + +static int mcb_match(struct device *dev, struct device_driver *drv) +{ + struct mcb_driver *mdrv = to_mcb_driver(drv); + struct mcb_device *mdev = to_mcb_device(dev); + const struct mcb_device_id *found_id; + + found_id = mcb_match_id(mdrv->id_table, mdev); + if (found_id) + return 1; + + return 0; +} + +static int mcb_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct mcb_device *mdev = to_mcb_device(dev); + int ret; + + ret = add_uevent_var(env, "MODALIAS=mcb:16z%03d", mdev->id); + if (ret) + return -ENOMEM; + + return 0; +} + +static int mcb_probe(struct device *dev) +{ + struct mcb_driver *mdrv = to_mcb_driver(dev->driver); + struct mcb_device *mdev = to_mcb_device(dev); + const struct mcb_device_id *found_id; + + found_id = mcb_match_id(mdrv->id_table, mdev); + if (!found_id) + return -ENODEV; + + return mdrv->probe(mdev, found_id); +} + +static int mcb_remove(struct device *dev) +{ + struct mcb_driver *mdrv = to_mcb_driver(dev->driver); + struct mcb_device *mdev = to_mcb_device(dev); + + mdrv->remove(mdev); + + put_device(&mdev->dev); + + return 0; +} + +static void mcb_shutdown(struct device *dev) +{ + struct mcb_device *mdev = to_mcb_device(dev); + struct mcb_driver *mdrv = mdev->driver; + + if (mdrv && mdrv->shutdown) + mdrv->shutdown(mdev); +} + +static struct bus_type mcb_bus_type = { + .name = "mcb", + .match = mcb_match, + .uevent = mcb_uevent, + .probe = mcb_probe, + .remove = mcb_remove, + .shutdown = mcb_shutdown, +}; + +/** + * __mcb_register_driver() - Register a @mcb_driver at the system + * @drv: The @mcb_driver + * @owner: The @mcb_driver's module + * @mod_name: The name of the @mcb_driver's module + * + * Register a @mcb_driver at the system. Perform some sanity checks, if + * the .probe and .remove methods are provided by the driver. + */ +int __mcb_register_driver(struct mcb_driver *drv, struct module *owner, + const char *mod_name) +{ + if (!drv->probe || !drv->remove) + return -EINVAL; + + drv->driver.owner = owner; + drv->driver.bus = &mcb_bus_type; + drv->driver.mod_name = mod_name; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(__mcb_register_driver); + +/** + * mcb_unregister_driver() - Unregister a @mcb_driver from the system + * @drv: The @mcb_driver + * + * Unregister a @mcb_driver from the system. + */ +void mcb_unregister_driver(struct mcb_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(mcb_unregister_driver); + +static void mcb_release_dev(struct device *dev) +{ + struct mcb_device *mdev = to_mcb_device(dev); + + mcb_bus_put(mdev->bus); + kfree(mdev); +} + +/** + * mcb_device_register() - Register a mcb_device + * @bus: The @mcb_bus of the device + * @dev: The @mcb_device + * + * Register a specific @mcb_device at a @mcb_bus and the system itself. + */ +int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev) +{ + int ret; + int device_id; + + device_initialize(&dev->dev); + dev->dev.bus = &mcb_bus_type; + dev->dev.parent = bus->dev.parent; + dev->dev.release = mcb_release_dev; + + device_id = dev->id; + dev_set_name(&dev->dev, "mcb%d-16z%03d-%d:%d:%d", + bus->bus_nr, device_id, dev->inst, dev->group, dev->var); + + ret = device_add(&dev->dev); + if (ret < 0) { + pr_err("Failed registering device 16z%03d on bus mcb%d (%d)\n", + device_id, bus->bus_nr, ret); + goto out; + } + + return 0; + +out: + + return ret; +} +EXPORT_SYMBOL_GPL(mcb_device_register); + +/** + * mcb_alloc_bus() - Allocate a new @mcb_bus + * + * Allocate a new @mcb_bus. + */ +struct mcb_bus *mcb_alloc_bus(void) +{ + struct mcb_bus *bus; + int bus_nr; + + bus = kzalloc(sizeof(struct mcb_bus), GFP_KERNEL); + if (!bus) + return NULL; + + bus_nr = ida_simple_get(&mcb_ida, 0, 0, GFP_KERNEL); + if (bus_nr < 0) { + kfree(bus); + return ERR_PTR(bus_nr); + } + + INIT_LIST_HEAD(&bus->children); + bus->bus_nr = bus_nr; + + return bus; +} +EXPORT_SYMBOL_GPL(mcb_alloc_bus); + +static int __mcb_devices_unregister(struct device *dev, void *data) +{ + device_unregister(dev); + return 0; +} + +static void mcb_devices_unregister(struct mcb_bus *bus) +{ + bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_devices_unregister); +} +/** + * mcb_release_bus() - Free a @mcb_bus + * @bus: The @mcb_bus to release + * + * Release an allocated @mcb_bus from the system. + */ +void mcb_release_bus(struct mcb_bus *bus) +{ + mcb_devices_unregister(bus); + + ida_simple_remove(&mcb_ida, bus->bus_nr); + + kfree(bus); +} +EXPORT_SYMBOL_GPL(mcb_release_bus); + +/** + * mcb_bus_put() - Increment refcnt + * @bus: The @mcb_bus + * + * Get a @mcb_bus' ref + */ +struct mcb_bus *mcb_bus_get(struct mcb_bus *bus) +{ + if (bus) + get_device(&bus->dev); + + return bus; +} +EXPORT_SYMBOL_GPL(mcb_bus_get); + +/** + * mcb_bus_put() - Decrement refcnt + * @bus: The @mcb_bus + * + * Release a @mcb_bus' ref + */ +void mcb_bus_put(struct mcb_bus *bus) +{ + if (bus) + put_device(&bus->dev); +} +EXPORT_SYMBOL_GPL(mcb_bus_put); + +/** + * mcb_alloc_dev() - Allocate a device + * @bus: The @mcb_bus the device is part of + * + * Allocate a @mcb_device and add bus. + */ +struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus) +{ + struct mcb_device *dev; + + dev = kzalloc(sizeof(struct mcb_device), GFP_KERNEL); + if (!dev) + return NULL; + + INIT_LIST_HEAD(&dev->bus_list); + dev->bus = bus; + + return dev; +} +EXPORT_SYMBOL_GPL(mcb_alloc_dev); + +/** + * mcb_free_dev() - Free @mcb_device + * @dev: The device to free + * + * Free a @mcb_device + */ +void mcb_free_dev(struct mcb_device *dev) +{ + kfree(dev); +} +EXPORT_SYMBOL_GPL(mcb_free_dev); + +static int __mcb_bus_add_devices(struct device *dev, void *data) +{ + struct mcb_device *mdev = to_mcb_device(dev); + int retval; + + if (mdev->is_added) + return 0; + + retval = device_attach(dev); + if (retval < 0) + dev_err(dev, "Error adding device (%d)\n", retval); + + mdev->is_added = true; + + return 0; +} + +static int __mcb_bus_add_child(struct device *dev, void *data) +{ + struct mcb_device *mdev = to_mcb_device(dev); + struct mcb_bus *child; + + BUG_ON(!mdev->is_added); + child = mdev->subordinate; + + if (child) + mcb_bus_add_devices(child); + + return 0; +} + +/** + * mcb_bus_add_devices() - Add devices in the bus' internal device list + * @bus: The @mcb_bus we add the devices + * + * Add devices in the bus' internal device list to the system. + */ +void mcb_bus_add_devices(const struct mcb_bus *bus) +{ + bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices); + bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_child); + +} +EXPORT_SYMBOL_GPL(mcb_bus_add_devices); + +/** + * mcb_request_mem() - Request memory + * @dev: The @mcb_device the memory is for + * @name: The name for the memory reference. + * + * Request memory for a @mcb_device. If @name is NULL the driver name will + * be used. + */ +struct resource *mcb_request_mem(struct mcb_device *dev, const char *name) +{ + struct resource *mem; + u32 size; + + if (!name) + name = dev->dev.driver->name; + + size = resource_size(&dev->mem); + + mem = request_mem_region(dev->mem.start, size, name); + if (!mem) + return ERR_PTR(-EBUSY); + + return mem; +} +EXPORT_SYMBOL_GPL(mcb_request_mem); + +/** + * mcb_release_mem() - Release memory requested by device + * @dev: The @mcb_device that requested the memory + * + * Release memory that was prior requested via @mcb_request_mem(). + */ +void mcb_release_mem(struct resource *mem) +{ + u32 size; + + size = resource_size(mem); + release_mem_region(mem->start, size); +} +EXPORT_SYMBOL_GPL(mcb_release_mem); + +/** + * mcb_get_irq() - Get device's IRQ number + * @dev: The @mcb_device the IRQ is for + * + * Get the IRQ number of a given @mcb_device. + */ +int mcb_get_irq(struct mcb_device *dev) +{ + struct resource *irq = &dev->irq; + + return irq->start; +} +EXPORT_SYMBOL_GPL(mcb_get_irq); + +static int mcb_init(void) +{ + return bus_register(&mcb_bus_type); +} + +static void mcb_exit(void) +{ + bus_unregister(&mcb_bus_type); +} + +/* mcb must be initialized after PCI but before the chameleon drivers. + * That means we must use some initcall between subsys_initcall and + * device_initcall. + */ +fs_initcall(mcb_init); +module_exit(mcb_exit); + +MODULE_DESCRIPTION("MEN Chameleon Bus Driver"); +MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mcb/mcb-internal.h b/drivers/mcb/mcb-internal.h new file mode 100644 index 000000000000..f956ef26c0ce --- /dev/null +++ b/drivers/mcb/mcb-internal.h @@ -0,0 +1,118 @@ +#ifndef __MCB_INTERNAL +#define __MCB_INTERNAL + +#include <linux/types.h> + +#define PCI_VENDOR_ID_MEN 0x1a88 +#define PCI_DEVICE_ID_MEN_CHAMELEON 0x4d45 +#define CHAMELEON_FILENAME_LEN 12 +#define CHAMELEONV2_MAGIC 0xabce + +enum chameleon_descriptor_type { + CHAMELEON_DTYPE_GENERAL = 0x0, + CHAMELEON_DTYPE_BRIDGE = 0x1, + CHAMELEON_DTYPE_CPU = 0x2, + CHAMELEON_DTYPE_BAR = 0x3, + CHAMELEON_DTYPE_END = 0xf, +}; + +enum chameleon_bus_type { + CHAMELEON_BUS_WISHBONE, + CHAMELEON_BUS_AVALON, + CHAMELEON_BUS_LPC, + CHAMELEON_BUS_ISA, +}; + +/** + * struct chameleon_fpga_header + * + * @revision: Revison of Chameleon table in FPGA + * @model: Chameleon table model ASCII char + * @minor: Revision minor + * @bus_type: Bus type (usually %CHAMELEON_BUS_WISHBONE) + * @magic: Chameleon header magic number (0xabce for version 2) + * @reserved: Reserved + * @filename: Filename of FPGA bitstream + */ +struct chameleon_fpga_header { + u8 revision; + char model; + u8 minor; + u8 bus_type; + u16 magic; + u16 reserved; + /* This one has no '\0' at the end!!! */ + char filename[CHAMELEON_FILENAME_LEN]; +} __packed; +#define HEADER_MAGIC_OFFSET 0x4 + +/** + * struct chameleon_gdd - Chameleon General Device Descriptor + * + * @irq: the position in the FPGA's IRQ controller vector + * @rev: the revision of the variant's implementation + * @var: the variant of the IP core + * @dev: the device the IP core is + * @dtype: device descriptor type + * @bar: BAR offset that must be added to module offset + * @inst: the instance number of the device, 0 is first instance + * @group: the group the device belongs to (0 = no group) + * @reserved: reserved + * @offset: beginning of the address window of desired module + * @size: size of the module's address window + */ +struct chameleon_gdd { + __le32 reg1; + __le32 reg2; + __le32 offset; + __le32 size; + +} __packed; + +/* GDD Register 1 fields */ +#define GDD_IRQ(x) ((x) & 0x1f) +#define GDD_REV(x) (((x) >> 5) & 0x3f) +#define GDD_VAR(x) (((x) >> 11) & 0x3f) +#define GDD_DEV(x) (((x) >> 18) & 0x3ff) +#define GDD_DTY(x) (((x) >> 28) & 0xf) + +/* GDD Register 2 fields */ +#define GDD_BAR(x) ((x) & 0x7) +#define GDD_INS(x) (((x) >> 3) & 0x3f) +#define GDD_GRP(x) (((x) >> 9) & 0x3f) + +/** + * struct chameleon_bdd - Chameleon Bridge Device Descriptor + * + * @irq: the position in the FPGA's IRQ controller vector + * @rev: the revision of the variant's implementation + * @var: the variant of the IP core + * @dev: the device the IP core is + * @dtype: device descriptor type + * @bar: BAR offset that must be added to module offset + * @inst: the instance number of the device, 0 is first instance + * @dbar: destination bar from the bus _behind_ the bridge + * @chamoff: offset within the BAR of the source bus + * @offset: + * @size: + */ +struct chameleon_bdd { + unsigned int irq:6; + unsigned int rev:6; + unsigned int var:6; + unsigned int dev:10; + unsigned int dtype:4; + unsigned int bar:3; + unsigned int inst:6; + unsigned int dbar:3; + unsigned int group:6; + unsigned int reserved:14; + u32 chamoff; + u32 offset; + u32 size; +} __packed; + +int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, + void __iomem *base); + +#endif diff --git a/drivers/mcb/mcb-parse.c b/drivers/mcb/mcb-parse.c new file mode 100644 index 000000000000..d1278b5f3028 --- /dev/null +++ b/drivers/mcb/mcb-parse.c @@ -0,0 +1,159 @@ +#include <linux/types.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/mcb.h> + +#include "mcb-internal.h" + +struct mcb_parse_priv { + phys_addr_t mapbase; + void __iomem *base; +}; + +#define for_each_chameleon_cell(dtype, p) \ + for ((dtype) = get_next_dtype((p)); \ + (dtype) != CHAMELEON_DTYPE_END; \ + (dtype) = get_next_dtype((p))) + +static inline uint32_t get_next_dtype(void __iomem *p) +{ + uint32_t dtype; + + dtype = readl(p); + return dtype >> 28; +} + +static int chameleon_parse_bdd(struct mcb_bus *bus, + phys_addr_t mapbase, + void __iomem *base) +{ + return 0; +} + +static int chameleon_parse_gdd(struct mcb_bus *bus, + phys_addr_t mapbase, + void __iomem *base) +{ + struct chameleon_gdd __iomem *gdd = + (struct chameleon_gdd __iomem *) base; + struct mcb_device *mdev; + u32 offset; + u32 size; + int ret; + __le32 reg1; + __le32 reg2; + + mdev = mcb_alloc_dev(bus); + if (!mdev) + return -ENOMEM; + + reg1 = readl(&gdd->reg1); + reg2 = readl(&gdd->reg2); + offset = readl(&gdd->offset); + size = readl(&gdd->size); + + mdev->id = GDD_DEV(reg1); + mdev->rev = GDD_REV(reg1); + mdev->var = GDD_VAR(reg1); + mdev->bar = GDD_BAR(reg1); + mdev->group = GDD_GRP(reg2); + mdev->inst = GDD_INS(reg2); + + pr_debug("Found a 16z%03d\n", mdev->id); + + mdev->irq.start = GDD_IRQ(reg1); + mdev->irq.end = GDD_IRQ(reg1); + mdev->irq.flags = IORESOURCE_IRQ; + + mdev->mem.start = mapbase + offset; + mdev->mem.end = mdev->mem.start + size - 1; + mdev->mem.flags = IORESOURCE_MEM; + + mdev->is_added = false; + + ret = mcb_device_register(bus, mdev); + if (ret < 0) + goto err; + + return 0; + +err: + mcb_free_dev(mdev); + + return ret; +} + +int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, + void __iomem *base) +{ + char __iomem *p = base; + struct chameleon_fpga_header *header; + uint32_t dtype; + int num_cells = 0; + int ret = 0; + u32 hsize; + + hsize = sizeof(struct chameleon_fpga_header); + + header = kzalloc(hsize, GFP_KERNEL); + if (!header) + return -ENOMEM; + + /* Extract header information */ + memcpy_fromio(header, p, hsize); + /* We only support chameleon v2 at the moment */ + header->magic = le16_to_cpu(header->magic); + if (header->magic != CHAMELEONV2_MAGIC) { + pr_err("Unsupported chameleon version 0x%x\n", + header->magic); + kfree(header); + return -ENODEV; + } + p += hsize; + + pr_debug("header->revision = %d\n", header->revision); + pr_debug("header->model = 0x%x ('%c')\n", header->model, + header->model); + pr_debug("header->minor = %d\n", header->minor); + pr_debug("header->bus_type = 0x%x\n", header->bus_type); + + + pr_debug("header->magic = 0x%x\n", header->magic); + pr_debug("header->filename = \"%.*s\"\n", CHAMELEON_FILENAME_LEN, + header->filename); + + for_each_chameleon_cell(dtype, p) { + switch (dtype) { + case CHAMELEON_DTYPE_GENERAL: + ret = chameleon_parse_gdd(bus, mapbase, p); + if (ret < 0) + goto out; + p += sizeof(struct chameleon_gdd); + break; + case CHAMELEON_DTYPE_BRIDGE: + chameleon_parse_bdd(bus, mapbase, p); + p += sizeof(struct chameleon_bdd); + break; + case CHAMELEON_DTYPE_END: + break; + default: + pr_err("Invalid chameleon descriptor type 0x%x\n", + dtype); + return -EINVAL; + } + num_cells++; + } + + if (num_cells == 0) + num_cells = -EINVAL; + + kfree(header); + return num_cells; + +out: + kfree(header); + return ret; +} +EXPORT_SYMBOL_GPL(chameleon_parse_cells); diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c new file mode 100644 index 000000000000..99c742cbfb5b --- /dev/null +++ b/drivers/mcb/mcb-pci.c @@ -0,0 +1,114 @@ +/* + * MEN Chameleon Bus. + * + * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) + * Author: Johannes Thumshirn <johannes.thumshirn@men.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; version 2 of the License. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/mcb.h> + +#include "mcb-internal.h" + +struct priv { + struct mcb_bus *bus; + void __iomem *base; +}; + +static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct priv *priv; + phys_addr_t mapbase; + int ret; + int num_cells; + unsigned long flags; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "Failed to enable PCI device\n"); + return -ENODEV; + } + + mapbase = pci_resource_start(pdev, 0); + if (!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"); + goto err_start; + } + + priv->base = pci_iomap(pdev, 0, 0); + if (!priv->base) { + dev_err(&pdev->dev, "Cannot ioremap\n"); + ret = -ENOMEM; + goto err_ioremap; + } + + flags = pci_resource_flags(pdev, 0); + if (flags & IORESOURCE_IO) { + ret = -ENOTSUPP; + dev_err(&pdev->dev, + "IO mapped PCI devices are not supported\n"); + goto err_ioremap; + } + + pci_set_drvdata(pdev, priv); + + priv->bus = mcb_alloc_bus(); + + ret = chameleon_parse_cells(priv->bus, mapbase, priv->base); + if (ret < 0) + goto err_drvdata; + num_cells = ret; + + dev_dbg(&pdev->dev, "Found %d cells\n", num_cells); + + mcb_bus_add_devices(priv->bus); + +err_drvdata: + pci_iounmap(pdev, priv->base); +err_ioremap: + pci_release_region(pdev, 0); +err_start: + pci_disable_device(pdev); + return ret; +} + +static void mcb_pci_remove(struct pci_dev *pdev) +{ + struct priv *priv = pci_get_drvdata(pdev); + + mcb_release_bus(priv->bus); +} + +static const struct pci_device_id mcb_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) }, + { 0 }, +}; +MODULE_DEVICE_TABLE(pci, mcb_pci_tbl); + +static struct pci_driver mcb_pci_driver = { + .name = "mcb-pci", + .id_table = mcb_pci_tbl, + .probe = mcb_pci_probe, + .remove = mcb_pci_remove, +}; + +module_pci_driver(mcb_pci_driver); + +MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MCB over PCI support"); diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 9a06fe883766..95ad936e6048 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -254,16 +254,6 @@ config DM_THIN_PROVISIONING ---help--- Provides thin provisioning and snapshots that share a data store. -config DM_DEBUG_BLOCK_STACK_TRACING - boolean "Keep stack trace of persistent data block lock holders" - depends on STACKTRACE_SUPPORT && DM_PERSISTENT_DATA - select STACKTRACE - ---help--- - Enable this for messages that may help debug problems with the - block manager locking used by thin provisioning and caching. - - If unsure, say N. - config DM_CACHE tristate "Cache target (EXPERIMENTAL)" depends on BLK_DEV_DM diff --git a/drivers/md/dm-cache-policy-mq.c b/drivers/md/dm-cache-policy-mq.c index 1e018e986610..0e385e40909e 100644 --- a/drivers/md/dm-cache-policy-mq.c +++ b/drivers/md/dm-cache-policy-mq.c @@ -872,7 +872,7 @@ static void mq_destroy(struct dm_cache_policy *p) { struct mq_policy *mq = to_mq_policy(p); - kfree(mq->table); + vfree(mq->table); epool_exit(&mq->cache_pool); epool_exit(&mq->pre_cache_pool); kfree(mq); @@ -1245,7 +1245,7 @@ static struct dm_cache_policy *mq_create(dm_cblock_t cache_size, mq->nr_buckets = next_power(from_cblock(cache_size) / 2, 16); mq->hash_bits = ffs(mq->nr_buckets) - 1; - mq->table = kzalloc(sizeof(*mq->table) * mq->nr_buckets, GFP_KERNEL); + mq->table = vzalloc(sizeof(*mq->table) * mq->nr_buckets); if (!mq->table) goto bad_alloc_table; diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 1af70145fab9..074b9c8e4cf0 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -979,12 +979,13 @@ static void issue_copy_real(struct dm_cache_migration *mg) int r; struct dm_io_region o_region, c_region; struct cache *cache = mg->cache; + sector_t cblock = from_cblock(mg->cblock); o_region.bdev = cache->origin_dev->bdev; o_region.count = cache->sectors_per_block; c_region.bdev = cache->cache_dev->bdev; - c_region.sector = from_cblock(mg->cblock) * cache->sectors_per_block; + c_region.sector = cblock * cache->sectors_per_block; c_region.count = cache->sectors_per_block; if (mg->writeback || mg->demote) { @@ -2464,20 +2465,18 @@ static int cache_map(struct dm_target *ti, struct bio *bio) bool discarded_block; struct dm_bio_prison_cell *cell; struct policy_result lookup_result; - struct per_bio_data *pb; + struct per_bio_data *pb = init_per_bio_data(bio, pb_data_size); - if (from_oblock(block) > from_oblock(cache->origin_blocks)) { + if (unlikely(from_oblock(block) >= from_oblock(cache->origin_blocks))) { /* * This can only occur if the io goes to a partial block at * the end of the origin device. We don't cache these. * Just remap to the origin and carry on. */ - remap_to_origin_clear_discard(cache, bio, block); + remap_to_origin(cache, bio); return DM_MAPIO_REMAPPED; } - pb = init_per_bio_data(bio, pb_data_size); - if (bio->bi_rw & (REQ_FLUSH | REQ_FUA | REQ_DISCARD)) { defer_bio(cache, bio); return DM_MAPIO_SUBMITTED; diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c index 08d9a207259a..b428c0ae63d5 100644 --- a/drivers/md/dm-log-userspace-transfer.c +++ b/drivers/md/dm-log-userspace-transfer.c @@ -66,7 +66,7 @@ static int dm_ulog_sendto_server(struct dm_ulog_request *tfr) msg->seq = tfr->seq; msg->len = sizeof(struct dm_ulog_request) + tfr->data_size; - r = cn_netlink_send(msg, 0, gfp_any()); + r = cn_netlink_send(msg, 0, 0, gfp_any()); return r; } diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index afc3d017de4c..d6e88178d22c 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -546,6 +546,9 @@ static int read_exceptions(struct pstore *ps, r = insert_exceptions(ps, area, callback, callback_context, &full); + if (!full) + memcpy(ps->area, area, ps->store->chunk_size << SECTOR_SHIFT); + dm_bufio_release(bp); dm_bufio_forget(client, chunk); diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index baa87ff12816..fb9efc829182 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -76,7 +76,7 @@ #define THIN_SUPERBLOCK_MAGIC 27022010 #define THIN_SUPERBLOCK_LOCATION 0 -#define THIN_VERSION 1 +#define THIN_VERSION 2 #define THIN_METADATA_CACHE_SIZE 64 #define SECTOR_TO_BLOCK_SHIFT 3 @@ -1755,3 +1755,38 @@ int dm_pool_register_metadata_threshold(struct dm_pool_metadata *pmd, return r; } + +int dm_pool_metadata_set_needs_check(struct dm_pool_metadata *pmd) +{ + int r; + struct dm_block *sblock; + struct thin_disk_superblock *disk_super; + + down_write(&pmd->root_lock); + pmd->flags |= THIN_METADATA_NEEDS_CHECK_FLAG; + + r = superblock_lock(pmd, &sblock); + if (r) { + DMERR("couldn't read superblock"); + goto out; + } + + disk_super = dm_block_data(sblock); + disk_super->flags = cpu_to_le32(pmd->flags); + + dm_bm_unlock(sblock); +out: + up_write(&pmd->root_lock); + return r; +} + +bool dm_pool_metadata_needs_check(struct dm_pool_metadata *pmd) +{ + bool needs_check; + + down_read(&pmd->root_lock); + needs_check = pmd->flags & THIN_METADATA_NEEDS_CHECK_FLAG; + up_read(&pmd->root_lock); + + return needs_check; +} diff --git a/drivers/md/dm-thin-metadata.h b/drivers/md/dm-thin-metadata.h index 82ea384d36ff..e3c857db195a 100644 --- a/drivers/md/dm-thin-metadata.h +++ b/drivers/md/dm-thin-metadata.h @@ -25,6 +25,11 @@ /*----------------------------------------------------------------*/ +/* + * Thin metadata superblock flags. + */ +#define THIN_METADATA_NEEDS_CHECK_FLAG (1 << 0) + struct dm_pool_metadata; struct dm_thin_device; @@ -202,6 +207,12 @@ int dm_pool_register_metadata_threshold(struct dm_pool_metadata *pmd, dm_sm_threshold_fn fn, void *context); +/* + * Updates the superblock immediately. + */ +int dm_pool_metadata_set_needs_check(struct dm_pool_metadata *pmd); +bool dm_pool_metadata_needs_check(struct dm_pool_metadata *pmd); + /*----------------------------------------------------------------*/ #endif diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 7e84baccf0ad..be70d38745f7 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -130,10 +130,11 @@ static void build_virtual_key(struct dm_thin_device *td, dm_block_t b, struct dm_thin_new_mapping; /* - * The pool runs in 3 modes. Ordered in degraded order for comparisons. + * The pool runs in 4 modes. Ordered in degraded order for comparisons. */ enum pool_mode { PM_WRITE, /* metadata may be changed */ + PM_OUT_OF_DATA_SPACE, /* metadata may be changed, though data may not be allocated */ PM_READ_ONLY, /* metadata may not be changed */ PM_FAIL, /* all I/O fails */ }; @@ -198,7 +199,6 @@ struct pool { }; static enum pool_mode get_pool_mode(struct pool *pool); -static void out_of_data_space(struct pool *pool); static void metadata_operation_failed(struct pool *pool, const char *op, int r); /* @@ -226,6 +226,7 @@ struct thin_c { struct pool *pool; struct dm_thin_device *td; + bool requeue_mode:1; }; /*----------------------------------------------------------------*/ @@ -369,14 +370,18 @@ struct dm_thin_endio_hook { struct dm_thin_new_mapping *overwrite_mapping; }; -static void __requeue_bio_list(struct thin_c *tc, struct bio_list *master) +static void requeue_bio_list(struct thin_c *tc, struct bio_list *master) { struct bio *bio; struct bio_list bios; + unsigned long flags; bio_list_init(&bios); + + spin_lock_irqsave(&tc->pool->lock, flags); bio_list_merge(&bios, master); bio_list_init(master); + spin_unlock_irqrestore(&tc->pool->lock, flags); while ((bio = bio_list_pop(&bios))) { struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook)); @@ -391,12 +396,26 @@ static void __requeue_bio_list(struct thin_c *tc, struct bio_list *master) static void requeue_io(struct thin_c *tc) { struct pool *pool = tc->pool; + + requeue_bio_list(tc, &pool->deferred_bios); + requeue_bio_list(tc, &pool->retry_on_resume_list); +} + +static void error_retry_list(struct pool *pool) +{ + struct bio *bio; unsigned long flags; + struct bio_list bios; + + bio_list_init(&bios); spin_lock_irqsave(&pool->lock, flags); - __requeue_bio_list(tc, &pool->deferred_bios); - __requeue_bio_list(tc, &pool->retry_on_resume_list); + bio_list_merge(&bios, &pool->retry_on_resume_list); + bio_list_init(&pool->retry_on_resume_list); spin_unlock_irqrestore(&pool->lock, flags); + + while ((bio = bio_list_pop(&bios))) + bio_io_error(bio); } /* @@ -925,13 +944,15 @@ static void check_low_water_mark(struct pool *pool, dm_block_t free_blocks) } } +static void set_pool_mode(struct pool *pool, enum pool_mode new_mode); + static int alloc_data_block(struct thin_c *tc, dm_block_t *result) { int r; dm_block_t free_blocks; struct pool *pool = tc->pool; - if (get_pool_mode(pool) != PM_WRITE) + if (WARN_ON(get_pool_mode(pool) != PM_WRITE)) return -EINVAL; r = dm_pool_get_free_block_count(pool->pmd, &free_blocks); @@ -958,7 +979,7 @@ static int alloc_data_block(struct thin_c *tc, dm_block_t *result) } if (!free_blocks) { - out_of_data_space(pool); + set_pool_mode(pool, PM_OUT_OF_DATA_SPACE); return -ENOSPC; } } @@ -988,15 +1009,32 @@ static void retry_on_resume(struct bio *bio) spin_unlock_irqrestore(&pool->lock, flags); } -static void handle_unserviceable_bio(struct pool *pool, struct bio *bio) +static bool should_error_unserviceable_bio(struct pool *pool) { - /* - * When pool is read-only, no cell locking is needed because - * nothing is changing. - */ - WARN_ON_ONCE(get_pool_mode(pool) != PM_READ_ONLY); + enum pool_mode m = get_pool_mode(pool); + + switch (m) { + case PM_WRITE: + /* Shouldn't get here */ + DMERR_LIMIT("bio unserviceable, yet pool is in PM_WRITE mode"); + return true; + + case PM_OUT_OF_DATA_SPACE: + return pool->pf.error_if_no_space; - if (pool->pf.error_if_no_space) + case PM_READ_ONLY: + case PM_FAIL: + return true; + default: + /* Shouldn't get here */ + DMERR_LIMIT("bio unserviceable, yet pool has an unknown mode"); + return true; + } +} + +static void handle_unserviceable_bio(struct pool *pool, struct bio *bio) +{ + if (should_error_unserviceable_bio(pool)) bio_io_error(bio); else retry_on_resume(bio); @@ -1007,11 +1045,20 @@ static void retry_bios_on_resume(struct pool *pool, struct dm_bio_prison_cell *c struct bio *bio; struct bio_list bios; + if (should_error_unserviceable_bio(pool)) { + cell_error(pool, cell); + return; + } + bio_list_init(&bios); cell_release(pool, cell, &bios); - while ((bio = bio_list_pop(&bios))) - handle_unserviceable_bio(pool, bio); + if (should_error_unserviceable_bio(pool)) + while ((bio = bio_list_pop(&bios))) + bio_io_error(bio); + else + while ((bio = bio_list_pop(&bios))) + retry_on_resume(bio); } static void process_discard(struct thin_c *tc, struct bio *bio) @@ -1296,6 +1343,11 @@ static void process_bio_read_only(struct thin_c *tc, struct bio *bio) } } +static void process_bio_success(struct thin_c *tc, struct bio *bio) +{ + bio_endio(bio, 0); +} + static void process_bio_fail(struct thin_c *tc, struct bio *bio) { bio_io_error(bio); @@ -1328,6 +1380,11 @@ static void process_deferred_bios(struct pool *pool) struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook)); struct thin_c *tc = h->tc; + if (tc->requeue_mode) { + bio_endio(bio, DM_ENDIO_REQUEUE); + continue; + } + /* * If we've got no free new_mapping structs, and processing * this bio might require one, we pause until there are some @@ -1394,51 +1451,134 @@ static void do_waker(struct work_struct *ws) /*----------------------------------------------------------------*/ +struct noflush_work { + struct work_struct worker; + struct thin_c *tc; + + atomic_t complete; + wait_queue_head_t wait; +}; + +static void complete_noflush_work(struct noflush_work *w) +{ + atomic_set(&w->complete, 1); + wake_up(&w->wait); +} + +static void do_noflush_start(struct work_struct *ws) +{ + struct noflush_work *w = container_of(ws, struct noflush_work, worker); + w->tc->requeue_mode = true; + requeue_io(w->tc); + complete_noflush_work(w); +} + +static void do_noflush_stop(struct work_struct *ws) +{ + struct noflush_work *w = container_of(ws, struct noflush_work, worker); + w->tc->requeue_mode = false; + complete_noflush_work(w); +} + +static void noflush_work(struct thin_c *tc, void (*fn)(struct work_struct *)) +{ + struct noflush_work w; + + INIT_WORK(&w.worker, fn); + w.tc = tc; + atomic_set(&w.complete, 0); + init_waitqueue_head(&w.wait); + + queue_work(tc->pool->wq, &w.worker); + + wait_event(w.wait, atomic_read(&w.complete)); +} + +/*----------------------------------------------------------------*/ + static enum pool_mode get_pool_mode(struct pool *pool) { return pool->pf.mode; } +static void notify_of_pool_mode_change(struct pool *pool, const char *new_mode) +{ + dm_table_event(pool->ti->table); + DMINFO("%s: switching pool to %s mode", + dm_device_name(pool->pool_md), new_mode); +} + static void set_pool_mode(struct pool *pool, enum pool_mode new_mode) { - int r; - enum pool_mode old_mode = pool->pf.mode; + struct pool_c *pt = pool->ti->private; + bool needs_check = dm_pool_metadata_needs_check(pool->pmd); + enum pool_mode old_mode = get_pool_mode(pool); + + /* + * Never allow the pool to transition to PM_WRITE mode if user + * intervention is required to verify metadata and data consistency. + */ + if (new_mode == PM_WRITE && needs_check) { + DMERR("%s: unable to switch pool to write mode until repaired.", + dm_device_name(pool->pool_md)); + if (old_mode != new_mode) + new_mode = old_mode; + else + new_mode = PM_READ_ONLY; + } + /* + * If we were in PM_FAIL mode, rollback of metadata failed. We're + * not going to recover without a thin_repair. So we never let the + * pool move out of the old mode. + */ + if (old_mode == PM_FAIL) + new_mode = old_mode; switch (new_mode) { case PM_FAIL: if (old_mode != new_mode) - DMERR("%s: switching pool to failure mode", - dm_device_name(pool->pool_md)); + notify_of_pool_mode_change(pool, "failure"); dm_pool_metadata_read_only(pool->pmd); pool->process_bio = process_bio_fail; pool->process_discard = process_bio_fail; pool->process_prepared_mapping = process_prepared_mapping_fail; pool->process_prepared_discard = process_prepared_discard_fail; + + error_retry_list(pool); break; case PM_READ_ONLY: if (old_mode != new_mode) - DMERR("%s: switching pool to read-only mode", - dm_device_name(pool->pool_md)); - r = dm_pool_abort_metadata(pool->pmd); - if (r) { - DMERR("%s: aborting transaction failed", - dm_device_name(pool->pool_md)); - new_mode = PM_FAIL; - set_pool_mode(pool, new_mode); - } else { - dm_pool_metadata_read_only(pool->pmd); - pool->process_bio = process_bio_read_only; - pool->process_discard = process_discard; - pool->process_prepared_mapping = process_prepared_mapping_fail; - pool->process_prepared_discard = process_prepared_discard_passdown; - } + notify_of_pool_mode_change(pool, "read-only"); + dm_pool_metadata_read_only(pool->pmd); + pool->process_bio = process_bio_read_only; + pool->process_discard = process_bio_success; + pool->process_prepared_mapping = process_prepared_mapping_fail; + pool->process_prepared_discard = process_prepared_discard_passdown; + + error_retry_list(pool); + break; + + case PM_OUT_OF_DATA_SPACE: + /* + * Ideally we'd never hit this state; the low water mark + * would trigger userland to extend the pool before we + * completely run out of data space. However, many small + * IOs to unprovisioned space can consume data space at an + * alarming rate. Adjust your low water mark if you're + * frequently seeing this mode. + */ + if (old_mode != new_mode) + notify_of_pool_mode_change(pool, "out-of-data-space"); + pool->process_bio = process_bio_read_only; + pool->process_discard = process_discard; + pool->process_prepared_mapping = process_prepared_mapping; + pool->process_prepared_discard = process_prepared_discard_passdown; break; case PM_WRITE: if (old_mode != new_mode) - DMINFO("%s: switching pool to write mode", - dm_device_name(pool->pool_md)); + notify_of_pool_mode_change(pool, "write"); dm_pool_metadata_read_write(pool->pmd); pool->process_bio = process_bio; pool->process_discard = process_discard; @@ -1448,32 +1588,35 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode) } pool->pf.mode = new_mode; + /* + * The pool mode may have changed, sync it so bind_control_target() + * doesn't cause an unexpected mode transition on resume. + */ + pt->adjusted_pf.mode = new_mode; } -/* - * Rather than calling set_pool_mode directly, use these which describe the - * reason for mode degradation. - */ -static void out_of_data_space(struct pool *pool) +static void abort_transaction(struct pool *pool) { - DMERR_LIMIT("%s: no free data space available.", - dm_device_name(pool->pool_md)); - set_pool_mode(pool, PM_READ_ONLY); + const char *dev_name = dm_device_name(pool->pool_md); + + DMERR_LIMIT("%s: aborting current metadata transaction", dev_name); + if (dm_pool_abort_metadata(pool->pmd)) { + DMERR("%s: failed to abort metadata transaction", dev_name); + set_pool_mode(pool, PM_FAIL); + } + + if (dm_pool_metadata_set_needs_check(pool->pmd)) { + DMERR("%s: failed to set 'needs_check' flag in metadata", dev_name); + set_pool_mode(pool, PM_FAIL); + } } static void metadata_operation_failed(struct pool *pool, const char *op, int r) { - dm_block_t free_blocks; - DMERR_LIMIT("%s: metadata operation '%s' failed: error = %d", dm_device_name(pool->pool_md), op, r); - if (r == -ENOSPC && - !dm_pool_get_free_metadata_block_count(pool->pmd, &free_blocks) && - !free_blocks) - DMERR_LIMIT("%s: no free metadata space available.", - dm_device_name(pool->pool_md)); - + abort_transaction(pool); set_pool_mode(pool, PM_READ_ONLY); } @@ -1524,6 +1667,11 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio) thin_hook_bio(tc, bio); + if (tc->requeue_mode) { + bio_endio(bio, DM_ENDIO_REQUEUE); + return DM_MAPIO_SUBMITTED; + } + if (get_pool_mode(tc->pool) == PM_FAIL) { bio_io_error(bio); return DM_MAPIO_SUBMITTED; @@ -1687,7 +1835,7 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti) /* * We want to make sure that a pool in PM_FAIL mode is never upgraded. */ - enum pool_mode old_mode = pool->pf.mode; + enum pool_mode old_mode = get_pool_mode(pool); enum pool_mode new_mode = pt->adjusted_pf.mode; /* @@ -1701,16 +1849,6 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti) pool->pf = pt->adjusted_pf; pool->low_water_blocks = pt->low_water_blocks; - /* - * If we were in PM_FAIL mode, rollback of metadata failed. We're - * not going to recover without a thin_repair. So we never let the - * pool move out of the old mode. On the other hand a PM_READ_ONLY - * may have been due to a lack of metadata or data space, and may - * now work (ie. if the underlying devices have been resized). - */ - if (old_mode == PM_FAIL) - new_mode = old_mode; - set_pool_mode(pool, new_mode); return 0; @@ -2253,6 +2391,12 @@ static int maybe_resize_data_dev(struct dm_target *ti, bool *need_commit) return -EINVAL; } else if (data_size > sb_data_size) { + if (dm_pool_metadata_needs_check(pool->pmd)) { + DMERR("%s: unable to grow the data device until repaired.", + dm_device_name(pool->pool_md)); + return 0; + } + if (sb_data_size) DMINFO("%s: growing the data device from %llu to %llu blocks", dm_device_name(pool->pool_md), @@ -2294,6 +2438,12 @@ static int maybe_resize_metadata_dev(struct dm_target *ti, bool *need_commit) return -EINVAL; } else if (metadata_dev_size > sb_metadata_dev_size) { + if (dm_pool_metadata_needs_check(pool->pmd)) { + DMERR("%s: unable to grow the metadata device until repaired.", + dm_device_name(pool->pool_md)); + return 0; + } + warn_if_metadata_device_too_big(pool->md_dev); DMINFO("%s: growing the metadata device from %llu to %llu blocks", dm_device_name(pool->pool_md), @@ -2681,7 +2831,9 @@ static void pool_status(struct dm_target *ti, status_type_t type, else DMEMIT("- "); - if (pool->pf.mode == PM_READ_ONLY) + if (pool->pf.mode == PM_OUT_OF_DATA_SPACE) + DMEMIT("out_of_data_space "); + else if (pool->pf.mode == PM_READ_ONLY) DMEMIT("ro "); else DMEMIT("rw "); @@ -2795,7 +2947,7 @@ static struct target_type pool_target = { .name = "thin-pool", .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE | DM_TARGET_IMMUTABLE, - .version = {1, 10, 0}, + .version = {1, 11, 0}, .module = THIS_MODULE, .ctr = pool_ctr, .dtr = pool_dtr, @@ -2997,10 +3149,23 @@ static int thin_endio(struct dm_target *ti, struct bio *bio, int err) return 0; } -static void thin_postsuspend(struct dm_target *ti) +static void thin_presuspend(struct dm_target *ti) { + struct thin_c *tc = ti->private; + if (dm_noflush_suspending(ti)) - requeue_io((struct thin_c *)ti->private); + noflush_work(tc, do_noflush_start); +} + +static void thin_postsuspend(struct dm_target *ti) +{ + struct thin_c *tc = ti->private; + + /* + * The dm_noflush_suspending flag has been cleared by now, so + * unfortunately we must always run this. + */ + noflush_work(tc, do_noflush_stop); } /* @@ -3085,12 +3250,13 @@ static int thin_iterate_devices(struct dm_target *ti, static struct target_type thin_target = { .name = "thin", - .version = {1, 10, 0}, + .version = {1, 11, 0}, .module = THIS_MODULE, .ctr = thin_ctr, .dtr = thin_dtr, .map = thin_map, .end_io = thin_endio, + .presuspend = thin_presuspend, .postsuspend = thin_postsuspend, .status = thin_status, .iterate_devices = thin_iterate_devices, diff --git a/drivers/md/persistent-data/Kconfig b/drivers/md/persistent-data/Kconfig index 19b268795415..0c2dec7aec20 100644 --- a/drivers/md/persistent-data/Kconfig +++ b/drivers/md/persistent-data/Kconfig @@ -6,3 +6,13 @@ config DM_PERSISTENT_DATA ---help--- Library providing immutable on-disk data structure support for device-mapper targets such as the thin provisioning target. + +config DM_DEBUG_BLOCK_STACK_TRACING + boolean "Keep stack trace of persistent data block lock holders" + depends on STACKTRACE_SUPPORT && DM_PERSISTENT_DATA + select STACKTRACE + ---help--- + Enable this for messages that may help debug problems with the + block manager locking used by thin provisioning and caching. + + If unsure, say N. diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c index e9bdd462f4f5..786b689bdfc7 100644 --- a/drivers/md/persistent-data/dm-space-map-metadata.c +++ b/drivers/md/persistent-data/dm-space-map-metadata.c @@ -91,6 +91,69 @@ struct block_op { dm_block_t block; }; +struct bop_ring_buffer { + unsigned begin; + unsigned end; + struct block_op bops[MAX_RECURSIVE_ALLOCATIONS + 1]; +}; + +static void brb_init(struct bop_ring_buffer *brb) +{ + brb->begin = 0; + brb->end = 0; +} + +static bool brb_empty(struct bop_ring_buffer *brb) +{ + return brb->begin == brb->end; +} + +static unsigned brb_next(struct bop_ring_buffer *brb, unsigned old) +{ + unsigned r = old + 1; + return (r >= (sizeof(brb->bops) / sizeof(*brb->bops))) ? 0 : r; +} + +static int brb_push(struct bop_ring_buffer *brb, + enum block_op_type type, dm_block_t b) +{ + struct block_op *bop; + unsigned next = brb_next(brb, brb->end); + + /* + * We don't allow the last bop to be filled, this way we can + * differentiate between full and empty. + */ + if (next == brb->begin) + return -ENOMEM; + + bop = brb->bops + brb->end; + bop->type = type; + bop->block = b; + + brb->end = next; + + return 0; +} + +static int brb_pop(struct bop_ring_buffer *brb, struct block_op *result) +{ + struct block_op *bop; + + if (brb_empty(brb)) + return -ENODATA; + + bop = brb->bops + brb->begin; + result->type = bop->type; + result->block = bop->block; + + brb->begin = brb_next(brb, brb->begin); + + return 0; +} + +/*----------------------------------------------------------------*/ + struct sm_metadata { struct dm_space_map sm; @@ -101,25 +164,20 @@ struct sm_metadata { unsigned recursion_count; unsigned allocated_this_transaction; - unsigned nr_uncommitted; - struct block_op uncommitted[MAX_RECURSIVE_ALLOCATIONS]; + struct bop_ring_buffer uncommitted; struct threshold threshold; }; static int add_bop(struct sm_metadata *smm, enum block_op_type type, dm_block_t b) { - struct block_op *op; + int r = brb_push(&smm->uncommitted, type, b); - if (smm->nr_uncommitted == MAX_RECURSIVE_ALLOCATIONS) { + if (r) { DMERR("too many recursive allocations"); return -ENOMEM; } - op = smm->uncommitted + smm->nr_uncommitted++; - op->type = type; - op->block = b; - return 0; } @@ -158,11 +216,17 @@ static int out(struct sm_metadata *smm) return -ENOMEM; } - if (smm->recursion_count == 1 && smm->nr_uncommitted) { - while (smm->nr_uncommitted && !r) { - smm->nr_uncommitted--; - r = commit_bop(smm, smm->uncommitted + - smm->nr_uncommitted); + if (smm->recursion_count == 1) { + while (!brb_empty(&smm->uncommitted)) { + struct block_op bop; + + r = brb_pop(&smm->uncommitted, &bop); + if (r) { + DMERR("bug in bop ring buffer"); + break; + } + + r = commit_bop(smm, &bop); if (r) break; } @@ -217,7 +281,8 @@ static int sm_metadata_get_nr_free(struct dm_space_map *sm, dm_block_t *count) static int sm_metadata_get_count(struct dm_space_map *sm, dm_block_t b, uint32_t *result) { - int r, i; + int r; + unsigned i; struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm); unsigned adjustment = 0; @@ -225,8 +290,10 @@ static int sm_metadata_get_count(struct dm_space_map *sm, dm_block_t b, * We may have some uncommitted adjustments to add. This list * should always be really short. */ - for (i = 0; i < smm->nr_uncommitted; i++) { - struct block_op *op = smm->uncommitted + i; + for (i = smm->uncommitted.begin; + i != smm->uncommitted.end; + i = brb_next(&smm->uncommitted, i)) { + struct block_op *op = smm->uncommitted.bops + i; if (op->block != b) continue; @@ -254,7 +321,8 @@ static int sm_metadata_get_count(struct dm_space_map *sm, dm_block_t b, static int sm_metadata_count_is_more_than_one(struct dm_space_map *sm, dm_block_t b, int *result) { - int r, i, adjustment = 0; + int r, adjustment = 0; + unsigned i; struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm); uint32_t rc; @@ -262,8 +330,11 @@ static int sm_metadata_count_is_more_than_one(struct dm_space_map *sm, * We may have some uncommitted adjustments to add. This list * should always be really short. */ - for (i = 0; i < smm->nr_uncommitted; i++) { - struct block_op *op = smm->uncommitted + i; + for (i = smm->uncommitted.begin; + i != smm->uncommitted.end; + i = brb_next(&smm->uncommitted, i)) { + + struct block_op *op = smm->uncommitted.bops + i; if (op->block != b) continue; @@ -671,7 +742,7 @@ int dm_sm_metadata_create(struct dm_space_map *sm, smm->begin = superblock + 1; smm->recursion_count = 0; smm->allocated_this_transaction = 0; - smm->nr_uncommitted = 0; + brb_init(&smm->uncommitted); threshold_init(&smm->threshold); memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm)); @@ -715,7 +786,7 @@ int dm_sm_metadata_open(struct dm_space_map *sm, smm->begin = 0; smm->recursion_count = 0; smm->allocated_this_transaction = 0; - smm->nr_uncommitted = 0; + brb_init(&smm->uncommitted); threshold_init(&smm->threshold); memcpy(&smm->old_ll, &smm->ll, sizeof(smm->old_ll)); diff --git a/drivers/media/pci/cx18/cx18-alsa-main.c b/drivers/media/pci/cx18/cx18-alsa-main.c index b2c8c3439fea..ea272bcb38df 100644 --- a/drivers/media/pci/cx18/cx18-alsa-main.c +++ b/drivers/media/pci/cx18/cx18-alsa-main.c @@ -145,11 +145,12 @@ static int snd_cx18_init(struct v4l2_device *v4l2_dev) /* This is a no-op for us. We'll use the cx->instance */ /* (2) Create a card instance */ - ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */ - SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ - THIS_MODULE, 0, &sc); + ret = snd_card_new(&cx->pci_dev->dev, + SNDRV_DEFAULT_IDX1, /* use first available id */ + SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ + THIS_MODULE, 0, &sc); if (ret) { - CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n", + CX18_ALSA_ERR("%s: snd_card_new() failed with err %d\n", __func__, ret); goto err_exit; } diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c index c6c9bd58f8be..554798dcedd0 100644 --- a/drivers/media/pci/cx23885/cx23885-alsa.c +++ b/drivers/media/pci/cx23885/cx23885-alsa.c @@ -489,7 +489,8 @@ struct cx23885_audio_dev *cx23885_audio_register(struct cx23885_dev *dev) return NULL; } - err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + err = snd_card_new(&dev->pci->dev, + SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, sizeof(struct cx23885_audio_dev), &card); if (err < 0) goto error; @@ -500,8 +501,6 @@ struct cx23885_audio_dev *cx23885_audio_register(struct cx23885_dev *dev) chip->card = card; spin_lock_init(&chip->lock); - snd_card_set_dev(card, &dev->pci->dev); - err = snd_cx23885_pcm(chip, 0, "CX23885 Digital"); if (err < 0) goto error; diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index b1e08c3e55cd..2dd5bcaa7e53 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -645,8 +645,9 @@ static int cx25821_audio_initdev(struct cx25821_dev *dev) return -ENOENT; } - err = snd_card_create(index[devno], id[devno], THIS_MODULE, - sizeof(struct cx25821_audio_dev), &card); + err = snd_card_new(&dev->pci->dev, index[devno], id[devno], + THIS_MODULE, + sizeof(struct cx25821_audio_dev), &card); if (err < 0) { pr_info("DEBUG ERROR: cannot create snd_card_new in %s\n", __func__); @@ -682,8 +683,6 @@ static int cx25821_audio_initdev(struct cx25821_dev *dev) goto error; } - snd_card_set_dev(card, &chip->pci->dev); - strcpy(card->shortname, "cx25821"); sprintf(card->longname, "%s at 0x%lx irq %d", chip->dev->name, chip->iobase, chip->irq); diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index d014206e7176..a72579a9f67f 100644 --- a/drivers/media/pci/cx88/cx88-alsa.c +++ b/drivers/media/pci/cx88/cx88-alsa.c @@ -852,8 +852,6 @@ static int snd_cx88_create(struct snd_card *card, struct pci_dev *pci, chip->irq = pci->irq; synchronize_irq(chip->irq); - snd_card_set_dev(card, &pci->dev); - *rchip = chip; *core_ptr = core; @@ -876,8 +874,8 @@ static int cx88_audio_initdev(struct pci_dev *pci, return (-ENOENT); } - err = snd_card_create(index[devno], id[devno], THIS_MODULE, - sizeof(snd_cx88_card_t), &card); + err = snd_card_new(&pci->dev, index[devno], id[devno], THIS_MODULE, + sizeof(snd_cx88_card_t), &card); if (err < 0) return err; diff --git a/drivers/media/pci/ivtv/ivtv-alsa-main.c b/drivers/media/pci/ivtv/ivtv-alsa-main.c index e970cface70e..39b52929755a 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-main.c +++ b/drivers/media/pci/ivtv/ivtv-alsa-main.c @@ -145,11 +145,12 @@ static int snd_ivtv_init(struct v4l2_device *v4l2_dev) /* This is a no-op for us. We'll use the itv->instance */ /* (2) Create a card instance */ - ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */ - SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ - THIS_MODULE, 0, &sc); + ret = snd_card_new(&itv->pdev->dev, + SNDRV_DEFAULT_IDX1, /* use first available id */ + SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ + THIS_MODULE, 0, &sc); if (ret) { - IVTV_ALSA_ERR("%s: snd_card_create() failed with err %d\n", + IVTV_ALSA_ERR("%s: snd_card_new() failed with err %d\n", __func__, ret); goto err_exit; } diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c index dd67c8a400cc..e04a4d5d6672 100644 --- a/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -1072,8 +1072,8 @@ static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) if (!enable[devnum]) return -ENODEV; - err = snd_card_create(index[devnum], id[devnum], THIS_MODULE, - sizeof(snd_card_saa7134_t), &card); + err = snd_card_new(&dev->pci->dev, index[devnum], id[devnum], + THIS_MODULE, sizeof(snd_card_saa7134_t), &card); if (err < 0) return err; @@ -1115,8 +1115,6 @@ static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) if ((err = snd_card_saa7134_pcm(chip, 0)) < 0) goto __nodev; - snd_card_set_dev(card, &chip->pci->dev); - /* End of "creation" */ strcpy(card->shortname, "SAA7134"); diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index 81a1d971d797..9b925874d392 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -665,8 +665,8 @@ static int cx231xx_audio_init(struct cx231xx *dev) cx231xx_info("cx231xx-audio.c: probing for cx231xx " "non standard usbaudio\n"); - err = snd_card_create(index[devnr], "Cx231xx Audio", THIS_MODULE, - 0, &card); + err = snd_card_new(&dev->udev->dev, index[devnr], "Cx231xx Audio", + THIS_MODULE, 0, &card); if (err < 0) return err; @@ -682,7 +682,6 @@ static int cx231xx_audio_init(struct cx231xx *dev) pcm->info_flags = 0; pcm->private_data = dev; strcpy(pcm->name, "Conexant cx231xx Capture"); - snd_card_set_dev(card, &dev->udev->dev); strcpy(card->driver, "Cx231xx-Audio"); strcpy(card->shortname, "Cx231xx Audio"); strcpy(card->longname, "Conexant cx231xx Audio"); diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 05e9bd11a3ff..1a28897af183 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -900,8 +900,8 @@ static int em28xx_audio_init(struct em28xx *dev) printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2014 Mauro Carvalho Chehab\n"); - err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0, - &card); + err = snd_card_new(&dev->udev->dev, index[devnr], "Em28xx Audio", + THIS_MODULE, 0, &card); if (err < 0) return err; @@ -918,7 +918,6 @@ static int em28xx_audio_init(struct em28xx *dev) pcm->private_data = dev; strcpy(pcm->name, "Empia 28xx Capture"); - snd_card_set_dev(card, &dev->udev->dev); strcpy(card->driver, "Em28xx-Audio"); strcpy(card->shortname, "Em28xx Audio"); strcpy(card->longname, "Empia Em28xx Audio"); diff --git a/drivers/media/usb/stk1160/stk1160-ac97.c b/drivers/media/usb/stk1160/stk1160-ac97.c index c8583c262c3d..c46c8be89602 100644 --- a/drivers/media/usb/stk1160/stk1160-ac97.c +++ b/drivers/media/usb/stk1160/stk1160-ac97.c @@ -98,13 +98,11 @@ int stk1160_ac97_register(struct stk1160 *dev) * Just want a card to access ac96 controls, * the actual capture interface will be handled by snd-usb-audio */ - rc = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); + rc = snd_card_new(dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); if (rc < 0) return rc; - snd_card_set_dev(card, dev->dev); - /* TODO: I'm not sure where should I get these names :-( */ snprintf(card->shortname, sizeof(card->shortname), "stk1160-mixer"); diff --git a/drivers/media/usb/tlg2300/pd-alsa.c b/drivers/media/usb/tlg2300/pd-alsa.c index 3f3e141f70fb..dd8fe100590f 100644 --- a/drivers/media/usb/tlg2300/pd-alsa.c +++ b/drivers/media/usb/tlg2300/pd-alsa.c @@ -300,7 +300,8 @@ int poseidon_audio_init(struct poseidon *p) struct snd_pcm *pcm; int ret; - ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card); + ret = snd_card_new(&p->interface->dev, -1, "Telegent", + THIS_MODULE, 0, &card); if (ret != 0) return ret; diff --git a/drivers/media/usb/tm6000/tm6000-alsa.c b/drivers/media/usb/tm6000/tm6000-alsa.c index 813c1ec53608..3239cd62e452 100644 --- a/drivers/media/usb/tm6000/tm6000-alsa.c +++ b/drivers/media/usb/tm6000/tm6000-alsa.c @@ -431,7 +431,8 @@ static int tm6000_audio_init(struct tm6000_core *dev) if (!enable[devnr]) return -ENOENT; - rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card); + rc = snd_card_new(&dev->udev->dev, index[devnr], "tm6000", + THIS_MODULE, 0, &card); if (rc < 0) { snd_printk(KERN_ERR "cannot create card instance %d\n", devnr); return rc; @@ -445,7 +446,6 @@ static int tm6000_audio_init(struct tm6000_core *dev) le16_to_cpu(dev->udev->descriptor.idVendor), le16_to_cpu(dev->udev->descriptor.idProduct)); snd_component_add(card, component); - snd_card_set_dev(card, &dev->udev->dev); chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL); if (!chip) { diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 29a11db365bc..c59e9c96e86d 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -7,6 +7,17 @@ menuconfig MEMORY if MEMORY +config TI_AEMIF + tristate "Texas Instruments AEMIF driver" + depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF + help + This driver is for the AEMIF module available in Texas Instruments + SoCs. AEMIF stands for Asynchronous External Memory Interface and + is intended to provide a glue-less interface to a variety of + asynchronuous memory devices like ASRAM, NOR and NAND memory. A total + of 256M bytes of any of these memories can be accessed at a given + time via four chip selects with 64M byte access per chip select. + config TI_EMIF tristate "Texas Instruments EMIF driver" depends on ARCH_OMAP2PLUS @@ -50,4 +61,8 @@ config TEGRA30_MC analysis, especially for IOMMU/SMMU(System Memory Management Unit) module. +config FSL_IFC + bool + depends on FSL_SOC + endif diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 969d923dad93..71160a2b7313 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -5,7 +5,9 @@ ifeq ($(CONFIG_DDR),y) obj-$(CONFIG_OF) += of_memory.o endif +obj-$(CONFIG_TI_AEMIF) += ti-aemif.o obj-$(CONFIG_TI_EMIF) += emif.o +obj-$(CONFIG_FSL_IFC) += fsl_ifc.o obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c new file mode 100644 index 000000000000..3d5d792d5cb2 --- /dev/null +++ b/drivers/memory/fsl_ifc.c @@ -0,0 +1,309 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc + * + * Freescale Integrated Flash Controller + * + * Author: Dipen Dudhat <Dipen.Dudhat@freescale.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. + * + * 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/module.h> +#include <linux/kernel.h> +#include <linux/compiler.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/fsl_ifc.h> +#include <asm/prom.h> + +struct fsl_ifc_ctrl *fsl_ifc_ctrl_dev; +EXPORT_SYMBOL(fsl_ifc_ctrl_dev); + +/* + * convert_ifc_address - convert the base address + * @addr_base: base address of the memory bank + */ +unsigned int convert_ifc_address(phys_addr_t addr_base) +{ + return addr_base & CSPR_BA; +} +EXPORT_SYMBOL(convert_ifc_address); + +/* + * fsl_ifc_find - find IFC bank + * @addr_base: base address of the memory bank + * + * This function walks IFC banks comparing "Base address" field of the CSPR + * registers with the supplied addr_base argument. When bases match this + * function returns bank number (starting with 0), otherwise it returns + * appropriate errno value. + */ +int fsl_ifc_find(phys_addr_t addr_base) +{ + int i = 0; + + if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(fsl_ifc_ctrl_dev->regs->cspr_cs); i++) { + u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); + if (cspr & CSPR_V && (cspr & CSPR_BA) == + convert_ifc_address(addr_base)) + return i; + } + + return -ENOENT; +} +EXPORT_SYMBOL(fsl_ifc_find); + +static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl) +{ + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + + /* + * Clear all the common status and event registers + */ + if (in_be32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER) + out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER); + + /* enable all error and events */ + out_be32(&ifc->cm_evter_en, IFC_CM_EVTER_EN_CSEREN); + + /* enable all error and event interrupts */ + out_be32(&ifc->cm_evter_intr_en, IFC_CM_EVTER_INTR_EN_CSERIREN); + out_be32(&ifc->cm_erattr0, 0x0); + out_be32(&ifc->cm_erattr1, 0x0); + + return 0; +} + +static int fsl_ifc_ctrl_remove(struct platform_device *dev) +{ + struct fsl_ifc_ctrl *ctrl = dev_get_drvdata(&dev->dev); + + free_irq(ctrl->nand_irq, ctrl); + free_irq(ctrl->irq, ctrl); + + irq_dispose_mapping(ctrl->nand_irq); + irq_dispose_mapping(ctrl->irq); + + iounmap(ctrl->regs); + + dev_set_drvdata(&dev->dev, NULL); + kfree(ctrl); + + return 0; +} + +/* + * NAND events are split between an operational interrupt which only + * receives OPC, and an error interrupt that receives everything else, + * including non-NAND errors. Whichever interrupt gets to it first + * records the status and wakes the wait queue. + */ +static DEFINE_SPINLOCK(nand_irq_lock); + +static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) +{ + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + unsigned long flags; + u32 stat; + + spin_lock_irqsave(&nand_irq_lock, flags); + + stat = in_be32(&ifc->ifc_nand.nand_evter_stat); + if (stat) { + out_be32(&ifc->ifc_nand.nand_evter_stat, stat); + ctrl->nand_stat = stat; + wake_up(&ctrl->nand_wait); + } + + spin_unlock_irqrestore(&nand_irq_lock, flags); + + return stat; +} + +static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data) +{ + struct fsl_ifc_ctrl *ctrl = data; + + if (check_nand_stat(ctrl)) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +/* + * NOTE: This interrupt is used to report ifc events of various kinds, + * such as transaction errors on the chipselects. + */ +static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) +{ + struct fsl_ifc_ctrl *ctrl = data; + struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + u32 err_axiid, err_srcid, status, cs_err, err_addr; + irqreturn_t ret = IRQ_NONE; + + /* read for chip select error */ + cs_err = in_be32(&ifc->cm_evter_stat); + if (cs_err) { + dev_err(ctrl->dev, "transaction sent to IFC is not mapped to" + "any memory bank 0x%08X\n", cs_err); + /* clear the chip select error */ + out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER); + + /* read error attribute registers print the error information */ + status = in_be32(&ifc->cm_erattr0); + err_addr = in_be32(&ifc->cm_erattr1); + + if (status & IFC_CM_ERATTR0_ERTYP_READ) + dev_err(ctrl->dev, "Read transaction error" + "CM_ERATTR0 0x%08X\n", status); + else + dev_err(ctrl->dev, "Write transaction error" + "CM_ERATTR0 0x%08X\n", status); + + err_axiid = (status & IFC_CM_ERATTR0_ERAID) >> + IFC_CM_ERATTR0_ERAID_SHIFT; + dev_err(ctrl->dev, "AXI ID of the error" + "transaction 0x%08X\n", err_axiid); + + err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >> + IFC_CM_ERATTR0_ESRCID_SHIFT; + dev_err(ctrl->dev, "SRC ID of the error" + "transaction 0x%08X\n", err_srcid); + + dev_err(ctrl->dev, "Transaction Address corresponding to error" + "ERADDR 0x%08X\n", err_addr); + + ret = IRQ_HANDLED; + } + + if (check_nand_stat(ctrl)) + ret = IRQ_HANDLED; + + return ret; +} + +/* + * fsl_ifc_ctrl_probe + * + * called by device layer when it finds a device matching + * one our driver can handled. This code allocates all of + * the resources needed for the controller only. The + * resources for the NAND banks themselves are allocated + * in the chip probe function. +*/ +static int fsl_ifc_ctrl_probe(struct platform_device *dev) +{ + int ret = 0; + + + dev_info(&dev->dev, "Freescale Integrated Flash Controller\n"); + + fsl_ifc_ctrl_dev = kzalloc(sizeof(*fsl_ifc_ctrl_dev), GFP_KERNEL); + if (!fsl_ifc_ctrl_dev) + return -ENOMEM; + + dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev); + + /* IOMAP the entire IFC region */ + fsl_ifc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); + if (!fsl_ifc_ctrl_dev->regs) { + dev_err(&dev->dev, "failed to get memory region\n"); + ret = -ENODEV; + goto err; + } + + /* get the Controller level irq */ + fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); + if (fsl_ifc_ctrl_dev->irq == NO_IRQ) { + dev_err(&dev->dev, "failed to get irq resource " + "for IFC\n"); + ret = -ENODEV; + goto err; + } + + /* get the nand machine irq */ + fsl_ifc_ctrl_dev->nand_irq = + irq_of_parse_and_map(dev->dev.of_node, 1); + + fsl_ifc_ctrl_dev->dev = &dev->dev; + + ret = fsl_ifc_ctrl_init(fsl_ifc_ctrl_dev); + if (ret < 0) + goto err; + + init_waitqueue_head(&fsl_ifc_ctrl_dev->nand_wait); + + ret = request_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_irq, IRQF_SHARED, + "fsl-ifc", fsl_ifc_ctrl_dev); + if (ret != 0) { + dev_err(&dev->dev, "failed to install irq (%d)\n", + fsl_ifc_ctrl_dev->irq); + goto err_irq; + } + + if (fsl_ifc_ctrl_dev->nand_irq) { + ret = request_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_nand_irq, + 0, "fsl-ifc-nand", fsl_ifc_ctrl_dev); + if (ret != 0) { + dev_err(&dev->dev, "failed to install irq (%d)\n", + fsl_ifc_ctrl_dev->nand_irq); + goto err_nandirq; + } + } + + return 0; + +err_nandirq: + free_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_ctrl_dev); + irq_dispose_mapping(fsl_ifc_ctrl_dev->nand_irq); +err_irq: + free_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_dev); + irq_dispose_mapping(fsl_ifc_ctrl_dev->irq); +err: + return ret; +} + +static const struct of_device_id fsl_ifc_match[] = { + { + .compatible = "fsl,ifc", + }, + {}, +}; + +static struct platform_driver fsl_ifc_ctrl_driver = { + .driver = { + .name = "fsl-ifc", + .of_match_table = fsl_ifc_match, + }, + .probe = fsl_ifc_ctrl_probe, + .remove = fsl_ifc_ctrl_remove, +}; + +static int __init fsl_ifc_init(void) +{ + return platform_driver_register(&fsl_ifc_ctrl_driver); +} +subsys_initcall(fsl_ifc_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale Semiconductor"); +MODULE_DESCRIPTION("Freescale Integrated Flash Controller driver"); diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c new file mode 100644 index 000000000000..d3df7602f406 --- /dev/null +++ b/drivers/memory/ti-aemif.c @@ -0,0 +1,427 @@ +/* + * TI AEMIF driver + * + * Copyright (C) 2010 - 2013 Texas Instruments Incorporated. http://www.ti.com/ + * + * Authors: + * Murali Karicheri <m-karicheri2@ti.com> + * Ivan Khoronzhuk <ivan.khoronzhuk@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/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> + +#define TA_SHIFT 2 +#define RHOLD_SHIFT 4 +#define RSTROBE_SHIFT 7 +#define RSETUP_SHIFT 13 +#define WHOLD_SHIFT 17 +#define WSTROBE_SHIFT 20 +#define WSETUP_SHIFT 26 +#define EW_SHIFT 30 +#define SS_SHIFT 31 + +#define TA(x) ((x) << TA_SHIFT) +#define RHOLD(x) ((x) << RHOLD_SHIFT) +#define RSTROBE(x) ((x) << RSTROBE_SHIFT) +#define RSETUP(x) ((x) << RSETUP_SHIFT) +#define WHOLD(x) ((x) << WHOLD_SHIFT) +#define WSTROBE(x) ((x) << WSTROBE_SHIFT) +#define WSETUP(x) ((x) << WSETUP_SHIFT) +#define EW(x) ((x) << EW_SHIFT) +#define SS(x) ((x) << SS_SHIFT) + +#define ASIZE_MAX 0x1 +#define TA_MAX 0x3 +#define RHOLD_MAX 0x7 +#define RSTROBE_MAX 0x3f +#define RSETUP_MAX 0xf +#define WHOLD_MAX 0x7 +#define WSTROBE_MAX 0x3f +#define WSETUP_MAX 0xf +#define EW_MAX 0x1 +#define SS_MAX 0x1 +#define NUM_CS 4 + +#define TA_VAL(x) (((x) & TA(TA_MAX)) >> TA_SHIFT) +#define RHOLD_VAL(x) (((x) & RHOLD(RHOLD_MAX)) >> RHOLD_SHIFT) +#define RSTROBE_VAL(x) (((x) & RSTROBE(RSTROBE_MAX)) >> RSTROBE_SHIFT) +#define RSETUP_VAL(x) (((x) & RSETUP(RSETUP_MAX)) >> RSETUP_SHIFT) +#define WHOLD_VAL(x) (((x) & WHOLD(WHOLD_MAX)) >> WHOLD_SHIFT) +#define WSTROBE_VAL(x) (((x) & WSTROBE(WSTROBE_MAX)) >> WSTROBE_SHIFT) +#define WSETUP_VAL(x) (((x) & WSETUP(WSETUP_MAX)) >> WSETUP_SHIFT) +#define EW_VAL(x) (((x) & EW(EW_MAX)) >> EW_SHIFT) +#define SS_VAL(x) (((x) & SS(SS_MAX)) >> SS_SHIFT) + +#define NRCSR_OFFSET 0x00 +#define AWCCR_OFFSET 0x04 +#define A1CR_OFFSET 0x10 + +#define ACR_ASIZE_MASK 0x3 +#define ACR_EW_MASK BIT(30) +#define ACR_SS_MASK BIT(31) +#define ASIZE_16BIT 1 + +#define CONFIG_MASK (TA(TA_MAX) | \ + RHOLD(RHOLD_MAX) | \ + RSTROBE(RSTROBE_MAX) | \ + RSETUP(RSETUP_MAX) | \ + WHOLD(WHOLD_MAX) | \ + WSTROBE(WSTROBE_MAX) | \ + WSETUP(WSETUP_MAX) | \ + EW(EW_MAX) | SS(SS_MAX) | \ + ASIZE_MAX) + +/** + * struct aemif_cs_data: structure to hold cs parameters + * @cs: chip-select number + * @wstrobe: write strobe width, ns + * @rstrobe: read strobe width, ns + * @wsetup: write setup width, ns + * @whold: write hold width, ns + * @rsetup: read setup width, ns + * @rhold: read hold width, ns + * @ta: minimum turn around time, ns + * @enable_ss: enable/disable select strobe mode + * @enable_ew: enable/disable extended wait mode + * @asize: width of the asynchronous device's data bus + */ +struct aemif_cs_data { + u8 cs; + u16 wstrobe; + u16 rstrobe; + u8 wsetup; + u8 whold; + u8 rsetup; + u8 rhold; + u8 ta; + u8 enable_ss; + u8 enable_ew; + u8 asize; +}; + +/** + * struct aemif_device: structure to hold device data + * @base: base address of AEMIF registers + * @clk: source clock + * @clk_rate: clock's rate in kHz + * @num_cs: number of assigned chip-selects + * @cs_offset: start number of cs nodes + * @cs_data: array of chip-select settings + */ +struct aemif_device { + void __iomem *base; + struct clk *clk; + unsigned long clk_rate; + u8 num_cs; + int cs_offset; + struct aemif_cs_data cs_data[NUM_CS]; +}; + +/** + * aemif_calc_rate - calculate timing data. + * @pdev: platform device to calculate for + * @wanted: The cycle time needed in nanoseconds. + * @clk: The input clock rate in kHz. + * @max: The maximum divider value that can be programmed. + * + * On success, returns the calculated timing value minus 1 for easy + * programming into AEMIF timing registers, else negative errno. + */ +static int aemif_calc_rate(struct platform_device *pdev, int wanted, + unsigned long clk, int max) +{ + int result; + + result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1; + + dev_dbg(&pdev->dev, "%s: result %d from %ld, %d\n", __func__, result, + clk, wanted); + + /* It is generally OK to have a more relaxed timing than requested... */ + if (result < 0) + result = 0; + + /* ... But configuring tighter timings is not an option. */ + else if (result > max) + result = -EINVAL; + + return result; +} + +/** + * aemif_config_abus - configure async bus parameters + * @pdev: platform device to configure for + * @csnum: aemif chip select number + * + * This function programs the given timing values (in real clock) into the + * AEMIF registers taking the AEMIF clock into account. + * + * This function does not use any locking while programming the AEMIF + * because it is expected that there is only one user of a given + * chip-select. + * + * Returns 0 on success, else negative errno. + */ +static int aemif_config_abus(struct platform_device *pdev, int csnum) +{ + struct aemif_device *aemif = platform_get_drvdata(pdev); + struct aemif_cs_data *data = &aemif->cs_data[csnum]; + int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup; + unsigned long clk_rate = aemif->clk_rate; + unsigned offset; + u32 set, val; + + offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4; + + ta = aemif_calc_rate(pdev, data->ta, clk_rate, TA_MAX); + rhold = aemif_calc_rate(pdev, data->rhold, clk_rate, RHOLD_MAX); + rstrobe = aemif_calc_rate(pdev, data->rstrobe, clk_rate, RSTROBE_MAX); + rsetup = aemif_calc_rate(pdev, data->rsetup, clk_rate, RSETUP_MAX); + whold = aemif_calc_rate(pdev, data->whold, clk_rate, WHOLD_MAX); + wstrobe = aemif_calc_rate(pdev, data->wstrobe, clk_rate, WSTROBE_MAX); + wsetup = aemif_calc_rate(pdev, data->wsetup, clk_rate, WSETUP_MAX); + + if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 || + whold < 0 || wstrobe < 0 || wsetup < 0) { + dev_err(&pdev->dev, "%s: cannot get suitable timings\n", + __func__); + return -EINVAL; + } + + set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) | + WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup); + + set |= (data->asize & ACR_ASIZE_MASK); + if (data->enable_ew) + set |= ACR_EW_MASK; + if (data->enable_ss) + set |= ACR_SS_MASK; + + val = readl(aemif->base + offset); + val &= ~CONFIG_MASK; + val |= set; + writel(val, aemif->base + offset); + + return 0; +} + +static inline int aemif_cycles_to_nsec(int val, unsigned long clk_rate) +{ + return ((val + 1) * NSEC_PER_MSEC) / clk_rate; +} + +/** + * aemif_get_hw_params - function to read hw register values + * @pdev: platform device to read for + * @csnum: aemif chip select number + * + * This function reads the defaults from the registers and update + * the timing values. Required for get/set commands and also for + * the case when driver needs to use defaults in hardware. + */ +static void aemif_get_hw_params(struct platform_device *pdev, int csnum) +{ + struct aemif_device *aemif = platform_get_drvdata(pdev); + struct aemif_cs_data *data = &aemif->cs_data[csnum]; + unsigned long clk_rate = aemif->clk_rate; + u32 val, offset; + + offset = A1CR_OFFSET + (data->cs - aemif->cs_offset) * 4; + val = readl(aemif->base + offset); + + data->ta = aemif_cycles_to_nsec(TA_VAL(val), clk_rate); + data->rhold = aemif_cycles_to_nsec(RHOLD_VAL(val), clk_rate); + data->rstrobe = aemif_cycles_to_nsec(RSTROBE_VAL(val), clk_rate); + data->rsetup = aemif_cycles_to_nsec(RSETUP_VAL(val), clk_rate); + data->whold = aemif_cycles_to_nsec(WHOLD_VAL(val), clk_rate); + data->wstrobe = aemif_cycles_to_nsec(WSTROBE_VAL(val), clk_rate); + data->wsetup = aemif_cycles_to_nsec(WSETUP_VAL(val), clk_rate); + data->enable_ew = EW_VAL(val); + data->enable_ss = SS_VAL(val); + data->asize = val & ASIZE_MAX; +} + +/** + * of_aemif_parse_abus_config - parse CS configuration from DT + * @pdev: platform device to parse for + * @np: device node ptr + * + * This function update the emif async bus configuration based on the values + * configured in a cs device binding node. + */ +static int of_aemif_parse_abus_config(struct platform_device *pdev, + struct device_node *np) +{ + struct aemif_device *aemif = platform_get_drvdata(pdev); + struct aemif_cs_data *data; + u32 cs; + u32 val; + + if (of_property_read_u32(np, "ti,cs-chipselect", &cs)) { + dev_dbg(&pdev->dev, "cs property is required"); + return -EINVAL; + } + + if (cs - aemif->cs_offset >= NUM_CS || cs < aemif->cs_offset) { + dev_dbg(&pdev->dev, "cs number is incorrect %d", cs); + return -EINVAL; + } + + if (aemif->num_cs >= NUM_CS) { + dev_dbg(&pdev->dev, "cs count is more than %d", NUM_CS); + return -EINVAL; + } + + data = &aemif->cs_data[aemif->num_cs]; + data->cs = cs; + + /* read the current value in the hw register */ + aemif_get_hw_params(pdev, aemif->num_cs++); + + /* override the values from device node */ + if (!of_property_read_u32(np, "ti,cs-min-turnaround-ns", &val)) + data->ta = val; + + if (!of_property_read_u32(np, "ti,cs-read-hold-ns", &val)) + data->rhold = val; + + if (!of_property_read_u32(np, "ti,cs-read-strobe-ns", &val)) + data->rstrobe = val; + + if (!of_property_read_u32(np, "ti,cs-read-setup-ns", &val)) + data->rsetup = val; + + if (!of_property_read_u32(np, "ti,cs-write-hold-ns", &val)) + data->whold = val; + + if (!of_property_read_u32(np, "ti,cs-write-strobe-ns", &val)) + data->wstrobe = val; + + if (!of_property_read_u32(np, "ti,cs-write-setup-ns", &val)) + data->wsetup = val; + + if (!of_property_read_u32(np, "ti,cs-bus-width", &val)) + if (val == 16) + data->asize = 1; + data->enable_ew = of_property_read_bool(np, "ti,cs-extended-wait-mode"); + data->enable_ss = of_property_read_bool(np, "ti,cs-select-strobe-mode"); + return 0; +} + +static const struct of_device_id aemif_of_match[] = { + { .compatible = "ti,davinci-aemif", }, + { .compatible = "ti,da850-aemif", }, + {}, +}; + +static int aemif_probe(struct platform_device *pdev) +{ + int i; + int ret = -ENODEV; + struct resource *res; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child_np; + struct aemif_device *aemif; + + if (np == NULL) + return 0; + + aemif = devm_kzalloc(dev, sizeof(*aemif), GFP_KERNEL); + if (!aemif) + return -ENOMEM; + + platform_set_drvdata(pdev, aemif); + + aemif->clk = devm_clk_get(dev, NULL); + if (IS_ERR(aemif->clk)) { + dev_err(dev, "cannot get clock 'aemif'\n"); + return PTR_ERR(aemif->clk); + } + + clk_prepare_enable(aemif->clk); + aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC; + + if (of_device_is_compatible(np, "ti,da850-aemif")) + aemif->cs_offset = 2; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + aemif->base = devm_ioremap_resource(dev, res); + if (IS_ERR(aemif->base)) { + ret = PTR_ERR(aemif->base); + goto error; + } + + /* + * For every controller device node, there is a cs device node that + * describe the bus configuration parameters. This functions iterate + * over these nodes and update the cs data array. + */ + for_each_available_child_of_node(np, child_np) { + ret = of_aemif_parse_abus_config(pdev, child_np); + if (ret < 0) + goto error; + } + + for (i = 0; i < aemif->num_cs; i++) { + ret = aemif_config_abus(pdev, i); + if (ret < 0) { + dev_err(dev, "Error configuring chip select %d\n", + aemif->cs_data[i].cs); + goto error; + } + } + + /* + * Create a child devices explicitly from here to + * guarantee that the child will be probed after the AEMIF timing + * parameters are set. + */ + for_each_available_child_of_node(np, child_np) { + ret = of_platform_populate(child_np, NULL, NULL, dev); + if (ret < 0) + goto error; + } + + return 0; +error: + clk_disable_unprepare(aemif->clk); + return ret; +} + +static int aemif_remove(struct platform_device *pdev) +{ + struct aemif_device *aemif = platform_get_drvdata(pdev); + + clk_disable_unprepare(aemif->clk); + return 0; +} + +static struct platform_driver aemif_driver = { + .probe = aemif_probe, + .remove = aemif_remove, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aemif_of_match), + }, +}; + +module_platform_driver(aemif_driver); + +MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>"); +MODULE_AUTHOR("Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>"); +MODULE_DESCRIPTION("Texas Instruments AEMIF driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" KBUILD_MODNAME); diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c index a8c08f332da0..92752fb5b2d3 100644 --- a/drivers/message/i2o/iop.c +++ b/drivers/message/i2o/iop.c @@ -652,6 +652,44 @@ static int i2o_iop_activate(struct i2o_controller *c) return i2o_hrt_get(c); }; +static void i2o_res_alloc(struct i2o_controller *c, unsigned long flags) +{ + i2o_status_block *sb = c->status_block.virt; + struct resource *res = &c->mem_resource; + resource_size_t size, align; + int err; + + res->name = c->pdev->bus->name; + res->flags = flags; + res->start = 0; + res->end = 0; + osm_info("%s: requires private memory resources.\n", c->name); + + if (flags & IORESOURCE_MEM) { + size = sb->desired_mem_size; + align = 1 << 20; /* unspecified, use 1Mb and play safe */ + } else { + size = sb->desired_io_size; + align = 1 << 12; /* unspecified, use 4Kb and play safe */ + } + + err = pci_bus_alloc_resource(c->pdev->bus, res, size, align, 0, 0, + NULL, NULL); + if (err < 0) + return; + + if (flags & IORESOURCE_MEM) { + c->mem_alloc = 1; + sb->current_mem_size = resource_size(res); + sb->current_mem_base = res->start; + } else if (flags & IORESOURCE_IO) { + c->io_alloc = 1; + sb->current_io_size = resource_size(res); + sb->current_io_base = res->start; + } + osm_info("%s: allocated PCI space %pR\n", c->name, res); +} + /** * i2o_iop_systab_set - Set the I2O System Table of the specified IOP * @c: I2O controller to which the system table should be send @@ -665,52 +703,13 @@ static int i2o_iop_systab_set(struct i2o_controller *c) struct i2o_message *msg; i2o_status_block *sb = c->status_block.virt; struct device *dev = &c->pdev->dev; - struct resource *root; int rc; - if (sb->current_mem_size < sb->desired_mem_size) { - struct resource *res = &c->mem_resource; - res->name = c->pdev->bus->name; - res->flags = IORESOURCE_MEM; - res->start = 0; - res->end = 0; - osm_info("%s: requires private memory resources.\n", c->name); - root = pci_find_parent_resource(c->pdev, res); - if (root == NULL) - osm_warn("%s: Can't find parent resource!\n", c->name); - if (root && allocate_resource(root, res, sb->desired_mem_size, sb->desired_mem_size, sb->desired_mem_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ - NULL, NULL) >= 0) { - c->mem_alloc = 1; - sb->current_mem_size = resource_size(res); - sb->current_mem_base = res->start; - osm_info("%s: allocated %llu bytes of PCI memory at " - "0x%016llX.\n", c->name, - (unsigned long long)resource_size(res), - (unsigned long long)res->start); - } - } + if (sb->current_mem_size < sb->desired_mem_size) + i2o_res_alloc(c, IORESOURCE_MEM); - if (sb->current_io_size < sb->desired_io_size) { - struct resource *res = &c->io_resource; - res->name = c->pdev->bus->name; - res->flags = IORESOURCE_IO; - res->start = 0; - res->end = 0; - osm_info("%s: requires private memory resources.\n", c->name); - root = pci_find_parent_resource(c->pdev, res); - if (root == NULL) - osm_warn("%s: Can't find parent resource!\n", c->name); - if (root && allocate_resource(root, res, sb->desired_io_size, sb->desired_io_size, sb->desired_io_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ - NULL, NULL) >= 0) { - c->io_alloc = 1; - sb->current_io_size = resource_size(res); - sb->current_mem_base = res->start; - osm_info("%s: allocated %llu bytes of PCI I/O at " - "0x%016llX.\n", c->name, - (unsigned long long)resource_size(res), - (unsigned long long)res->start); - } - } + if (sb->current_io_size < sb->desired_io_size) + i2o_res_alloc(c, IORESOURCE_IO); msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET); if (IS_ERR(msg)) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index a45aab9f6bb1..1c3ae57082ed 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -251,8 +251,6 @@ static int arizona_apply_hardware_patch(struct arizona* arizona) unsigned int fll, sysclk; int ret, err; - regcache_cache_bypass(arizona->regmap, true); - /* Cache existing FLL and SYSCLK settings */ ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll); if (ret != 0) { @@ -322,8 +320,6 @@ err_fll: err); } - regcache_cache_bypass(arizona->regmap, false); - if (ret != 0) return ret; else diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index 714e2135210e..281a82747275 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -26,7 +26,9 @@ #include <linux/mfd/samsung/core.h> #include <linux/mfd/samsung/irq.h> #include <linux/mfd/samsung/rtc.h> +#include <linux/mfd/samsung/s2mpa01.h> #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s5m8763.h> #include <linux/mfd/samsung/s5m8767.h> #include <linux/regmap.h> @@ -69,18 +71,53 @@ static const struct mfd_cell s2mps11_devs[] = { } }; +static const struct mfd_cell s2mps14_devs[] = { + { + .name = "s2mps14-pmic", + }, { + .name = "s2mps14-rtc", + }, { + .name = "s2mps14-clk", + } +}; + +static const struct mfd_cell s2mpa01_devs[] = { + { + .name = "s2mpa01-pmic", + }, +}; + #ifdef CONFIG_OF static struct of_device_id sec_dt_match[] = { { .compatible = "samsung,s5m8767-pmic", .data = (void *)S5M8767X, - }, - { .compatible = "samsung,s2mps11-pmic", + }, { + .compatible = "samsung,s2mps11-pmic", .data = (void *)S2MPS11X, + }, { + .compatible = "samsung,s2mps14-pmic", + .data = (void *)S2MPS14X, + }, { + .compatible = "samsung,s2mpa01-pmic", + .data = (void *)S2MPA01, + }, { + /* Sentinel */ }, - {}, }; #endif +static bool s2mpa01_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case S2MPA01_REG_INT1M: + case S2MPA01_REG_INT2M: + case S2MPA01_REG_INT3M: + return false; + default: + return true; + } +} + static bool s2mps11_volatile(struct device *dev, unsigned int reg) { switch (reg) { @@ -111,6 +148,15 @@ static const struct regmap_config sec_regmap_config = { .val_bits = 8, }; +static const struct regmap_config s2mpa01_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S2MPA01_REG_LDO_OVCB4, + .volatile_reg = s2mpa01_volatile, + .cache_type = REGCACHE_FLAT, +}; + static const struct regmap_config s2mps11_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -120,6 +166,15 @@ static const struct regmap_config s2mps11_regmap_config = { .cache_type = REGCACHE_FLAT, }; +static const struct regmap_config s2mps14_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S2MPS14_REG_LDODSCH3, + .volatile_reg = s2mps11_volatile, + .cache_type = REGCACHE_FLAT, +}; + static const struct regmap_config s5m8763_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -138,9 +193,18 @@ static const struct regmap_config s5m8767_regmap_config = { .cache_type = REGCACHE_FLAT, }; -static const struct regmap_config sec_rtc_regmap_config = { +static const struct regmap_config s5m_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = SEC_RTC_REG_MAX, +}; + +static const struct regmap_config s2mps14_rtc_regmap_config = { .reg_bits = 8, .val_bits = 8, + + .max_register = S2MPS_RTC_REG_MAX, }; #ifdef CONFIG_OF @@ -180,24 +244,24 @@ static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata( } #endif -static inline int sec_i2c_get_driver_data(struct i2c_client *i2c, +static inline unsigned long sec_i2c_get_driver_data(struct i2c_client *i2c, const struct i2c_device_id *id) { #ifdef CONFIG_OF if (i2c->dev.of_node) { const struct of_device_id *match; match = of_match_node(sec_dt_match, i2c->dev.of_node); - return (int)match->data; + return (unsigned long)match->data; } #endif - return (int)id->driver_data; + return id->driver_data; } static int sec_pmic_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct sec_platform_data *pdata = dev_get_platdata(&i2c->dev); - const struct regmap_config *regmap; + const struct regmap_config *regmap, *regmap_rtc; struct sec_pmic_dev *sec_pmic; int ret; @@ -229,17 +293,34 @@ static int sec_pmic_probe(struct i2c_client *i2c, } switch (sec_pmic->device_type) { + case S2MPA01: + regmap = &s2mpa01_regmap_config; + break; case S2MPS11X: regmap = &s2mps11_regmap_config; + /* + * The rtc-s5m driver does not support S2MPS11 and there + * is no mfd_cell for S2MPS11 RTC device. + * However we must pass something to devm_regmap_init_i2c() + * so use S5M-like regmap config even though it wouldn't work. + */ + regmap_rtc = &s5m_rtc_regmap_config; + break; + case S2MPS14X: + regmap = &s2mps14_regmap_config; + regmap_rtc = &s2mps14_rtc_regmap_config; break; case S5M8763X: regmap = &s5m8763_regmap_config; + regmap_rtc = &s5m_rtc_regmap_config; break; case S5M8767X: regmap = &s5m8767_regmap_config; + regmap_rtc = &s5m_rtc_regmap_config; break; default: regmap = &sec_regmap_config; + regmap_rtc = &s5m_rtc_regmap_config; break; } @@ -252,10 +333,13 @@ static int sec_pmic_probe(struct i2c_client *i2c, } sec_pmic->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR); + if (!sec_pmic->rtc) { + dev_err(&i2c->dev, "Failed to allocate I2C for RTC\n"); + return -ENODEV; + } i2c_set_clientdata(sec_pmic->rtc, sec_pmic); - sec_pmic->regmap_rtc = devm_regmap_init_i2c(sec_pmic->rtc, - &sec_rtc_regmap_config); + sec_pmic->regmap_rtc = devm_regmap_init_i2c(sec_pmic->rtc, regmap_rtc); if (IS_ERR(sec_pmic->regmap_rtc)) { ret = PTR_ERR(sec_pmic->regmap_rtc); dev_err(&i2c->dev, "Failed to allocate RTC register map: %d\n", @@ -283,10 +367,18 @@ static int sec_pmic_probe(struct i2c_client *i2c, ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs, ARRAY_SIZE(s5m8767_devs), NULL, 0, NULL); break; + case S2MPA01: + ret = mfd_add_devices(sec_pmic->dev, -1, s2mpa01_devs, + ARRAY_SIZE(s2mpa01_devs), NULL, 0, NULL); + break; case S2MPS11X: ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs, ARRAY_SIZE(s2mps11_devs), NULL, 0, NULL); break; + case S2MPS14X: + ret = mfd_add_devices(sec_pmic->dev, -1, s2mps14_devs, + ARRAY_SIZE(s2mps14_devs), NULL, 0, NULL); + break; default: /* If this happens the probe function is problem */ BUG(); diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index 4de494f51d40..64e7913aadc6 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -1,7 +1,7 @@ /* * sec-irq.c * - * Copyright (c) 2011 Samsung Electronics Co., Ltd + * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd * http://www.samsung.com * * This program is free software; you can redistribute it and/or modify it @@ -19,6 +19,7 @@ #include <linux/mfd/samsung/core.h> #include <linux/mfd/samsung/irq.h> #include <linux/mfd/samsung/s2mps11.h> +#include <linux/mfd/samsung/s2mps14.h> #include <linux/mfd/samsung/s5m8763.h> #include <linux/mfd/samsung/s5m8767.h> @@ -59,13 +60,13 @@ static const struct regmap_irq s2mps11_irqs[] = { .reg_offset = 1, .mask = S2MPS11_IRQ_RTC60S_MASK, }, - [S2MPS11_IRQ_RTCA1] = { + [S2MPS11_IRQ_RTCA0] = { .reg_offset = 1, - .mask = S2MPS11_IRQ_RTCA1_MASK, + .mask = S2MPS11_IRQ_RTCA0_MASK, }, - [S2MPS11_IRQ_RTCA2] = { + [S2MPS11_IRQ_RTCA1] = { .reg_offset = 1, - .mask = S2MPS11_IRQ_RTCA2_MASK, + .mask = S2MPS11_IRQ_RTCA1_MASK, }, [S2MPS11_IRQ_SMPL] = { .reg_offset = 1, @@ -89,6 +90,76 @@ static const struct regmap_irq s2mps11_irqs[] = { }, }; +static const struct regmap_irq s2mps14_irqs[] = { + [S2MPS14_IRQ_PWRONF] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_PWRONF_MASK, + }, + [S2MPS14_IRQ_PWRONR] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_PWRONR_MASK, + }, + [S2MPS14_IRQ_JIGONBF] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_JIGONBF_MASK, + }, + [S2MPS14_IRQ_JIGONBR] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_JIGONBR_MASK, + }, + [S2MPS14_IRQ_ACOKBF] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_ACOKBF_MASK, + }, + [S2MPS14_IRQ_ACOKBR] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_ACOKBR_MASK, + }, + [S2MPS14_IRQ_PWRON1S] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_PWRON1S_MASK, + }, + [S2MPS14_IRQ_MRB] = { + .reg_offset = 0, + .mask = S2MPS11_IRQ_MRB_MASK, + }, + [S2MPS14_IRQ_RTC60S] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_RTC60S_MASK, + }, + [S2MPS14_IRQ_RTCA1] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_RTCA1_MASK, + }, + [S2MPS14_IRQ_RTCA0] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_RTCA0_MASK, + }, + [S2MPS14_IRQ_SMPL] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_SMPL_MASK, + }, + [S2MPS14_IRQ_RTC1S] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_RTC1S_MASK, + }, + [S2MPS14_IRQ_WTSR] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_WTSR_MASK, + }, + [S2MPS14_IRQ_INT120C] = { + .reg_offset = 2, + .mask = S2MPS11_IRQ_INT120C_MASK, + }, + [S2MPS14_IRQ_INT140C] = { + .reg_offset = 2, + .mask = S2MPS11_IRQ_INT140C_MASK, + }, + [S2MPS14_IRQ_TSD] = { + .reg_offset = 2, + .mask = S2MPS14_IRQ_TSD_MASK, + }, +}; static const struct regmap_irq s5m8767_irqs[] = { [S5M8767_IRQ_PWRR] = { @@ -246,6 +317,16 @@ static const struct regmap_irq_chip s2mps11_irq_chip = { .ack_base = S2MPS11_REG_INT1, }; +static const struct regmap_irq_chip s2mps14_irq_chip = { + .name = "s2mps14", + .irqs = s2mps14_irqs, + .num_irqs = ARRAY_SIZE(s2mps14_irqs), + .num_regs = 3, + .status_base = S2MPS14_REG_INT1, + .mask_base = S2MPS14_REG_INT1M, + .ack_base = S2MPS14_REG_INT1, +}; + static const struct regmap_irq_chip s5m8767_irq_chip = { .name = "s5m8767", .irqs = s5m8767_irqs, @@ -297,6 +378,12 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic) sec_pmic->irq_base, &s2mps11_irq_chip, &sec_pmic->irq_data); break; + case S2MPS14X: + ret = regmap_add_irq_chip(sec_pmic->regmap_pmic, sec_pmic->irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + sec_pmic->irq_base, &s2mps14_irq_chip, + &sec_pmic->irq_data); + break; default: dev_err(sec_pmic->dev, "Unknown device type %d\n", sec_pmic->device_type); diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 1e9a4b2102f9..bffc584e4a43 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -80,8 +80,7 @@ static const struct reg_default wm5102_revb_patch[] = { int wm5102_patch(struct arizona *arizona) { const struct reg_default *wm5102_patch; - int ret = 0; - int i, patch_size; + int patch_size; switch (arizona->rev) { case 0: @@ -92,21 +91,9 @@ int wm5102_patch(struct arizona *arizona) patch_size = ARRAY_SIZE(wm5102_revb_patch); } - regcache_cache_bypass(arizona->regmap, true); - - for (i = 0; i < patch_size; i++) { - ret = regmap_write(arizona->regmap, wm5102_patch[i].reg, - wm5102_patch[i].def); - if (ret != 0) { - dev_err(arizona->dev, "Failed to write %x = %x: %d\n", - wm5102_patch[i].reg, wm5102_patch[i].def, ret); - goto out; - } - } - -out: - regcache_cache_bypass(arizona->regmap, false); - return ret; + return regmap_multi_reg_write_bypassed(arizona->regmap, + wm5102_patch, + patch_size); } static const struct regmap_irq wm5102_aod_irqs[ARIZONA_NUM_IRQ] = { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 6cb388e8fb7d..809afebe0dad 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -235,7 +235,7 @@ config SGI_XP config CS5535_MFGPT tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support" - depends on PCI && X86 && MFD_CS5535 + depends on MFD_CS5535 default n help This driver provides access to MFGPT functionality for other diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c index d3eee113baeb..a43053daad0e 100644 --- a/drivers/misc/ad525x_dpot.c +++ b/drivers/misc/ad525x_dpot.c @@ -72,7 +72,6 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index 0c6e037153d2..c6cc3dc8ae1f 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -22,7 +22,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/err.h> diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 5be808406edc..22de13727641 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -150,6 +150,12 @@ static int ssc_probe(struct platform_device *pdev) return -ENODEV; ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat; + if (pdev->dev.of_node) { + struct device_node *np = pdev->dev.of_node; + ssc->clk_from_rk_pin = + of_property_read_bool(np, "atmel,clk-from-rk-pin"); + } + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); ssc->regs = devm_ioremap_resource(&pdev->dev, regs); if (IS_ERR(ssc->regs)) diff --git a/drivers/misc/bmp085.c b/drivers/misc/bmp085.c index 820e53d0048f..9b313f7810f5 100644 --- a/drivers/misc/bmp085.c +++ b/drivers/misc/bmp085.c @@ -47,7 +47,6 @@ #include <linux/module.h> #include <linux/device.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/of.h> #include "bmp085.h" diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 9e2b985293fc..14d90eae605b 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -101,7 +101,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/poll.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/kref.h> #include <linux/io.h> diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index 154b02e5094f..6a672f9ef522 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -32,7 +32,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/i2c.h> #include <linux/string.h> #include <linux/list.h> diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 4f3bca1003a1..634f72929e12 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -10,7 +10,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/delay.h> diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index f0fa4e8ca124..33f8673d23a6 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -17,7 +17,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/jiffies.h> diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index 78e55b501c94..9ebeacdb8ec4 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -11,7 +11,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index e36157d5d3ab..580ff9df5529 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -27,7 +27,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/i2c.h> diff --git a/drivers/misc/eeprom/sunxi_sid.c b/drivers/misc/eeprom/sunxi_sid.c index 9c34e5704304..3f2b625b2032 100644 --- a/drivers/misc/eeprom/sunxi_sid.c +++ b/drivers/misc/eeprom/sunxi_sid.c @@ -21,7 +21,6 @@ #include <linux/err.h> #include <linux/export.h> #include <linux/fs.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/kobject.h> @@ -96,7 +95,7 @@ static int sunxi_sid_remove(struct platform_device *pdev) } static const struct of_device_id sunxi_sid_of_match[] = { - { .compatible = "allwinner,sun4i-sid", .data = (void *)16}, + { .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16}, { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512}, {/* sentinel */}, }; diff --git a/drivers/misc/genwqe/card_debugfs.c b/drivers/misc/genwqe/card_debugfs.c index 3bfdc07a7248..50d2096ea1c7 100644 --- a/drivers/misc/genwqe/card_debugfs.c +++ b/drivers/misc/genwqe/card_debugfs.c @@ -26,7 +26,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/uaccess.h> diff --git a/drivers/misc/hmc6352.c b/drivers/misc/hmc6352.c index 170bd3daf336..90520d76633f 100644 --- a/drivers/misc/hmc6352.c +++ b/drivers/misc/hmc6352.c @@ -22,7 +22,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/err.h> diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index e3183f26216b..12c30b486b27 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -26,7 +26,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/mutex.h> diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index b7f84dacf822..4a9c50a43afb 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -23,7 +23,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/err.h> diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 61fbe6acabef..0a1565e63c71 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -12,7 +12,6 @@ #include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/spi/spi.h> #include <linux/platform_device.h> #include <linux/delay.h> diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index 036effe9a795..3ef4627f9cb1 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -23,7 +23,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> -#include <linux/init.h> #include <linux/dmi.h> #include <linux/module.h> #include <linux/types.h> diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c index 7c97550240f1..d324f8a97b88 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -26,7 +26,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/pm_runtime.h> diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c index 9aa2bd2a71ae..bd06d0cfac45 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c @@ -10,7 +10,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/err.h> #include <linux/input.h> #include <linux/interrupt.h> diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 49c7a23f02fc..d66a2f24f6b3 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c @@ -30,6 +30,7 @@ * * See Documentation/fault-injection/provoke-crashes.txt for instructions */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> #include <linux/fs.h> @@ -45,6 +46,7 @@ #include <linux/debugfs.h> #include <linux/vmalloc.h> #include <linux/mman.h> +#include <asm/cacheflush.h> #ifdef CONFIG_IDE #include <linux/ide.h> @@ -101,6 +103,7 @@ enum ctype { CT_EXEC_USERSPACE, CT_ACCESS_USERSPACE, CT_WRITE_RO, + CT_WRITE_KERN, }; static char* cp_name[] = { @@ -137,6 +140,7 @@ static char* cp_type[] = { "EXEC_USERSPACE", "ACCESS_USERSPACE", "WRITE_RO", + "WRITE_KERN", }; static struct jprobe lkdtm; @@ -316,6 +320,13 @@ static void do_nothing(void) return; } +/* Must immediately follow do_nothing for size calculuations to work out. */ +static void do_overwritten(void) +{ + pr_info("do_overwritten wasn't overwritten!\n"); + return; +} + static noinline void corrupt_stack(void) { /* Use default char array length that triggers stack protection. */ @@ -328,7 +339,12 @@ static void execute_location(void *dst) { void (*func)(void) = dst; + pr_info("attempting ok execution at %p\n", do_nothing); + do_nothing(); + memcpy(dst, do_nothing, EXEC_SIZE); + flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE); + pr_info("attempting bad execution at %p\n", func); func(); } @@ -337,8 +353,13 @@ static void execute_user_location(void *dst) /* Intentionally crossing kernel/user memory boundary. */ void (*func)(void) = dst; + pr_info("attempting ok execution at %p\n", do_nothing); + do_nothing(); + if (copy_to_user((void __user *)dst, do_nothing, EXEC_SIZE)) return; + flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE); + pr_info("attempting bad execution at %p\n", func); func(); } @@ -463,8 +484,12 @@ static void lkdtm_do_action(enum ctype which) } ptr = (unsigned long *)user_addr; + + pr_info("attempting bad read at %p\n", ptr); tmp = *ptr; tmp += 0xc0dec0de; + + pr_info("attempting bad write at %p\n", ptr); *ptr = tmp; vm_munmap(user_addr, PAGE_SIZE); @@ -475,10 +500,28 @@ static void lkdtm_do_action(enum ctype which) unsigned long *ptr; ptr = (unsigned long *)&rodata; + + pr_info("attempting bad write at %p\n", ptr); *ptr ^= 0xabcd1234; break; } + case CT_WRITE_KERN: { + size_t size; + unsigned char *ptr; + + size = (unsigned long)do_overwritten - + (unsigned long)do_nothing; + ptr = (unsigned char *)do_overwritten; + + pr_info("attempting bad %zu byte write at %p\n", size, ptr); + memcpy(ptr, (unsigned char *)do_nothing, size); + flush_icache_range((unsigned long)ptr, + (unsigned long)(ptr + size)); + + do_overwritten(); + break; + } case CT_NONE: default: break; @@ -493,8 +536,8 @@ static void lkdtm_handler(void) spin_lock_irqsave(&count_lock, flags); count--; - printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n", - cp_name_to_str(cpoint), cp_type_to_str(cptype), count); + pr_info("Crash point %s of type %s hit, trigger in %d rounds\n", + cp_name_to_str(cpoint), cp_type_to_str(cptype), count); if (count == 0) { do_it = true; @@ -551,18 +594,18 @@ static int lkdtm_register_cpoint(enum cname which) lkdtm.kp.symbol_name = "generic_ide_ioctl"; lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl; #else - printk(KERN_INFO "lkdtm: Crash point not available\n"); + pr_info("Crash point not available\n"); return -EINVAL; #endif break; default: - printk(KERN_INFO "lkdtm: Invalid Crash Point\n"); + pr_info("Invalid Crash Point\n"); return -EINVAL; } cpoint = which; if ((ret = register_jprobe(&lkdtm)) < 0) { - printk(KERN_INFO "lkdtm: Couldn't register jprobe\n"); + pr_info("Couldn't register jprobe\n"); cpoint = CN_INVALID; } @@ -709,8 +752,7 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf, if (type == CT_NONE) return -EINVAL; - printk(KERN_INFO "lkdtm: Performing direct entry %s\n", - cp_type_to_str(type)); + pr_info("Performing direct entry %s\n", cp_type_to_str(type)); lkdtm_do_action(type); *off += count; @@ -772,7 +814,7 @@ static int __init lkdtm_module_init(void) /* Register debugfs interface */ lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL); if (!lkdtm_debugfs_root) { - printk(KERN_ERR "lkdtm: creating root dir failed\n"); + pr_err("creating root dir failed\n"); return -ENODEV; } @@ -787,28 +829,26 @@ static int __init lkdtm_module_init(void) de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root, NULL, &cur->fops); if (de == NULL) { - printk(KERN_ERR "lkdtm: could not create %s\n", - cur->name); + pr_err("could not create %s\n", cur->name); goto out_err; } } if (lkdtm_parse_commandline() == -EINVAL) { - printk(KERN_INFO "lkdtm: Invalid command\n"); + pr_info("Invalid command\n"); goto out_err; } if (cpoint != CN_INVALID && cptype != CT_NONE) { ret = lkdtm_register_cpoint(cpoint); if (ret < 0) { - printk(KERN_INFO "lkdtm: Invalid crash point %d\n", - cpoint); + pr_info("Invalid crash point %d\n", cpoint); goto out_err; } - printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n", - cpoint_name, cpoint_type); + pr_info("Crash point %s of type %s registered\n", + cpoint_name, cpoint_type); } else { - printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n"); + pr_info("No crash points registered, enable through debugfs\n"); } return 0; @@ -823,7 +863,7 @@ static void __exit lkdtm_module_exit(void) debugfs_remove_recursive(lkdtm_debugfs_root); unregister_jprobe(&lkdtm); - printk(KERN_INFO "lkdtm: Crash point unregistered\n"); + pr_info("Crash point unregistered\n"); } module_init(lkdtm_module_init); diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index c76fa31e9bf6..d23384dde73b 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -34,3 +34,12 @@ config INTEL_MEI_ME 82Q33 Express 82X38/X48 Express +config INTEL_MEI_TXE + tristate "Intel Trusted Execution Environment with ME Interface" + select INTEL_MEI + depends on X86 && PCI && WATCHDOG_CORE + help + MEI Support for Trusted Execution Environment device on Intel SoCs + + Supported SoCs: + Intel Bay Trail diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 08698a466268..8ebc6cda1373 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -1,6 +1,6 @@ # # Makefile - Intel Management Engine Interface (Intel MEI) Linux driver -# Copyright (c) 2010-2011, Intel Corporation. +# Copyright (c) 2010-2014, Intel Corporation. # obj-$(CONFIG_INTEL_MEI) += mei.o mei-objs := init.o @@ -17,3 +17,7 @@ mei-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o mei-me-objs := pci-me.o mei-me-objs += hw-me.o + +obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o +mei-txe-objs := pci-txe.o +mei-txe-objs += hw-txe.o diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 2fad84432829..b8deb3455480 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -21,7 +21,6 @@ #include <linux/fcntl.h> #include <linux/aio.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/list.h> @@ -35,7 +34,6 @@ #include "mei_dev.h" #include "hbm.h" -#include "hw-me.h" #include "client.h" const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, @@ -79,10 +77,9 @@ int mei_amthif_host_init(struct mei_device *dev) i = mei_me_cl_by_uuid(dev, &mei_amthif_guid); if (i < 0) { - ret = i; dev_info(&dev->pdev->dev, - "amthif: failed to find the client %d\n", ret); - return ret; + "amthif: failed to find the client %d\n", i); + return -ENOTTY; } cl->me_client_id = dev->me_clients[i].client_id; @@ -116,14 +113,11 @@ int mei_amthif_host_init(struct mei_device *dev) cl->state = MEI_FILE_CONNECTING; - if (mei_hbm_cl_connect_req(dev, cl)) { - dev_dbg(&dev->pdev->dev, "amthif: Failed to connect to ME client\n"); - cl->state = MEI_FILE_DISCONNECTED; - cl->host_client_id = 0; - } else { - cl->timer_count = MEI_CONNECT_TIMEOUT; - } - return 0; + ret = mei_cl_connect(cl, NULL); + + dev->iamthif_state = MEI_IAMTHIF_IDLE; + + return ret; } /** @@ -137,14 +131,12 @@ int mei_amthif_host_init(struct mei_device *dev) struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, struct file *file) { - struct mei_cl_cb *pos = NULL; - struct mei_cl_cb *next = NULL; + struct mei_cl_cb *cb; - list_for_each_entry_safe(pos, next, - &dev->amthif_rd_complete_list.list, list) { - if (pos->cl && pos->cl == &dev->iamthif_cl && - pos->file_object == file) - return pos; + list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) { + if (cb->cl && cb->cl == &dev->iamthif_cl && + cb->file_object == file) + return cb; } return NULL; } @@ -180,14 +172,13 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, /* Only possible if we are in timeout */ if (!cl || cl != &dev->iamthif_cl) { dev_dbg(&dev->pdev->dev, "bad file ext.\n"); - return -ETIMEDOUT; + return -ETIME; } i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id); - if (i < 0) { dev_dbg(&dev->pdev->dev, "amthif client not found.\n"); - return -ENODEV; + return -ENOTTY; } dev_dbg(&dev->pdev->dev, "checking amthif data\n"); cb = mei_amthif_find_read_list_entry(dev, file); @@ -228,7 +219,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, dev_dbg(&dev->pdev->dev, "amthif Time out\n"); /* 15 sec for the message has expired */ list_del(&cb->list); - rets = -ETIMEDOUT; + rets = -ETIME; goto free; } } @@ -253,9 +244,10 @@ int mei_amthif_read(struct mei_device *dev, struct file *file, * the buf_idx may point beyond */ length = min_t(size_t, length, (cb->buf_idx - *offset)); - if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) + if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { + dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n"); rets = -EFAULT; - else { + } else { rets = length; if ((*offset + length) < cb->buf_idx) { *offset += length; @@ -302,9 +294,8 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) if (ret < 0) return ret; - if (ret && dev->hbuf_is_ready) { + if (ret && mei_hbuf_acquire(dev)) { ret = 0; - dev->hbuf_is_ready = false; if (cb->request_buffer.size > mei_hbuf_max_len(dev)) { mei_hdr.length = mei_hbuf_max_len(dev); mei_hdr.msg_complete = 0; @@ -336,10 +327,6 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) list_add_tail(&cb->list, &dev->write_list.list); } } else { - if (!dev->hbuf_is_ready) - dev_dbg(&dev->pdev->dev, "host buffer is not empty"); - - dev_dbg(&dev->pdev->dev, "No flow control credentials, so add iamthif cb to write list.\n"); list_add_tail(&cb->list, &dev->write_list.list); } return 0; @@ -365,7 +352,7 @@ int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) if (ret) return ret; - cb->fop_type = MEI_FOP_IOCTL; + cb->fop_type = MEI_FOP_WRITE; if (!list_empty(&dev->amthif_cmd_list.list) || dev->iamthif_state != MEI_IAMTHIF_IDLE) { @@ -447,23 +434,23 @@ unsigned int mei_amthif_poll(struct mei_device *dev, /** - * mei_amthif_irq_write_completed - processes completed iamthif operation. + * mei_amthif_irq_write - write iamthif command in irq thread context. * * @dev: the device structure. - * @slots: free slots. * @cb_pos: callback block. * @cl: private data of the file object. * @cmpl_list: complete list. * * returns 0, OK; otherwise, error. */ -int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, - s32 *slots, struct mei_cl_cb *cmpl_list) +int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, + struct mei_cl_cb *cmpl_list) { struct mei_device *dev = cl->dev; struct mei_msg_hdr mei_hdr; size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; u32 msg_slots = mei_data2slots(len); + int slots; int rets; rets = mei_cl_flow_ctrl_creds(cl); @@ -480,13 +467,15 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, mei_hdr.reserved = 0; mei_hdr.internal = 0; - if (*slots >= msg_slots) { + slots = mei_hbuf_empty_slots(dev); + + if (slots >= msg_slots) { mei_hdr.length = len; mei_hdr.msg_complete = 1; /* Split the message only if we can write the whole host buffer */ - } else if (*slots == dev->hbuf_depth) { - msg_slots = *slots; - len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); + } else if (slots == dev->hbuf_depth) { + msg_slots = slots; + len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); mei_hdr.length = len; mei_hdr.msg_complete = 0; } else { @@ -496,7 +485,6 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); - *slots -= msg_slots; rets = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf + dev->iamthif_msg_buf_index); if (rets) { diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 4bc7d620d695..ddc5ac92a200 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -26,7 +26,6 @@ #include <linux/mei_cl_bus.h> #include "mei_dev.h" -#include "hw-me.h" #include "client.h" #define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) @@ -145,9 +144,9 @@ static struct device_type mei_cl_device_type = { static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev, uuid_le uuid) { - struct mei_cl *cl, *next; + struct mei_cl *cl; - list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { + list_for_each_entry(cl, &dev->device_list, device_link) { if (!uuid_le_cmp(uuid, cl->device_uuid)) return cl; } @@ -524,6 +523,22 @@ void mei_cl_bus_rx_event(struct mei_cl *cl) schedule_work(&device->event_work); } +void mei_cl_bus_remove_devices(struct mei_device *dev) +{ + struct mei_cl *cl, *next; + + mutex_lock(&dev->device_lock); + list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { + if (cl->device) + mei_cl_remove_device(cl->device); + + list_del(&cl->device_link); + mei_cl_unlink(cl); + kfree(cl); + } + mutex_unlock(&dev->device_lock); +} + int __init mei_cl_bus_init(void) { return bus_register(&mei_cl_bus_type); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 89a557972d1b..8c078b808cd3 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -29,20 +29,21 @@ * mei_me_cl_by_uuid - locate index of me client * * @dev: mei device + * + * Locking: called under "dev->device_lock" lock + * * returns me client index or -ENOENT if not found */ int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid) { - int i, res = -ENOENT; + int i; for (i = 0; i < dev->me_clients_num; ++i) if (uuid_le_cmp(*uuid, - dev->me_clients[i].props.protocol_name) == 0) { - res = i; - break; - } + dev->me_clients[i].props.protocol_name) == 0) + return i; - return res; + return -ENOENT; } @@ -60,37 +61,79 @@ int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid) int mei_me_cl_by_id(struct mei_device *dev, u8 client_id) { int i; + for (i = 0; i < dev->me_clients_num; i++) if (dev->me_clients[i].client_id == client_id) - break; - if (WARN_ON(dev->me_clients[i].client_id != client_id)) - return -ENOENT; + return i; - if (i == dev->me_clients_num) - return -ENOENT; - - return i; + return -ENOENT; } /** - * mei_io_list_flush - removes list entry belonging to cl. + * mei_cl_cmp_id - tells if the clients are the same * - * @list: An instance of our list structure - * @cl: host client + * @cl1: host client 1 + * @cl2: host client 2 + * + * returns true - if the clients has same host and me ids + * false - otherwise + */ +static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, + const struct mei_cl *cl2) +{ + return cl1 && cl2 && + (cl1->host_client_id == cl2->host_client_id) && + (cl1->me_client_id == cl2->me_client_id); +} + +/** + * mei_io_list_flush - removes cbs belonging to cl. + * + * @list: an instance of our list structure + * @cl: host client, can be NULL for flushing the whole list + * @free: whether to free the cbs */ -void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +static void __mei_io_list_flush(struct mei_cl_cb *list, + struct mei_cl *cl, bool free) { struct mei_cl_cb *cb; struct mei_cl_cb *next; + /* enable removing everything if no cl is specified */ list_for_each_entry_safe(cb, next, &list->list, list) { - if (cb->cl && mei_cl_cmp_id(cl, cb->cl)) + if (!cl || (cb->cl && mei_cl_cmp_id(cl, cb->cl))) { list_del(&cb->list); + if (free) + mei_io_cb_free(cb); + } } } /** + * mei_io_list_flush - removes list entry belonging to cl. + * + * @list: An instance of our list structure + * @cl: host client + */ +static inline void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) +{ + __mei_io_list_flush(list, cl, false); +} + + +/** + * mei_io_list_free - removes cb belonging to cl and free them + * + * @list: An instance of our list structure + * @cl: host client + */ +static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl) +{ + __mei_io_list_flush(list, cl, true); +} + +/** * mei_io_cb_free - free mei_cb_private related memory * * @cb: mei callback struct @@ -196,8 +239,8 @@ int mei_cl_flush_queues(struct mei_cl *cl) cl_dbg(dev, cl, "remove list entry belonging to cl\n"); mei_io_list_flush(&cl->dev->read_list, cl); - mei_io_list_flush(&cl->dev->write_list, cl); - mei_io_list_flush(&cl->dev->write_waiting_list, cl); + mei_io_list_free(&cl->dev->write_list, cl); + mei_io_list_free(&cl->dev->write_waiting_list, cl); mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); @@ -254,10 +297,9 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev) struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) { struct mei_device *dev = cl->dev; - struct mei_cl_cb *cb = NULL; - struct mei_cl_cb *next = NULL; + struct mei_cl_cb *cb; - list_for_each_entry_safe(cb, next, &dev->read_list.list, list) + list_for_each_entry(cb, &dev->read_list.list, list) if (mei_cl_cmp_id(cl, cb->cl)) return cb; return NULL; @@ -375,6 +417,23 @@ void mei_host_client_init(struct work_struct *work) mutex_unlock(&dev->device_lock); } +/** + * mei_hbuf_acquire: try to acquire host buffer + * + * @dev: the device structure + * returns true if host buffer was acquired + */ +bool mei_hbuf_acquire(struct mei_device *dev) +{ + if (!dev->hbuf_is_ready) { + dev_dbg(&dev->pdev->dev, "hbuf is not ready\n"); + return false; + } + + dev->hbuf_is_ready = false; + + return true; +} /** * mei_cl_disconnect - disconnect host client from the me one @@ -406,8 +465,7 @@ int mei_cl_disconnect(struct mei_cl *cl) return -ENOMEM; cb->fop_type = MEI_FOP_CLOSE; - if (dev->hbuf_is_ready) { - dev->hbuf_is_ready = false; + if (mei_hbuf_acquire(dev)) { if (mei_hbm_cl_disconnect_req(dev, cl)) { rets = -ENODEV; cl_err(dev, cl, "failed to disconnect.\n"); @@ -461,17 +519,17 @@ free: bool mei_cl_is_other_connecting(struct mei_cl *cl) { struct mei_device *dev; - struct mei_cl *pos; - struct mei_cl *next; + struct mei_cl *ocl; /* the other client */ if (WARN_ON(!cl || !cl->dev)) return false; dev = cl->dev; - list_for_each_entry_safe(pos, next, &dev->file_list, link) { - if ((pos->state == MEI_FILE_CONNECTING) && - (pos != cl) && cl->me_client_id == pos->me_client_id) + list_for_each_entry(ocl, &dev->file_list, link) { + if (ocl->state == MEI_FILE_CONNECTING && + ocl != cl && + cl->me_client_id == ocl->me_client_id) return true; } @@ -505,11 +563,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) goto out; } - cb->fop_type = MEI_FOP_IOCTL; - - if (dev->hbuf_is_ready && !mei_cl_is_other_connecting(cl)) { - dev->hbuf_is_ready = false; + cb->fop_type = MEI_FOP_CONNECT; + /* run hbuf acquire last so we don't have to undo */ + if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) { if (mei_hbm_cl_connect_req(dev, cl)) { rets = -ENODEV; goto out; @@ -521,18 +578,19 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) } mutex_unlock(&dev->device_lock); - rets = wait_event_timeout(dev->wait_recvd_msg, - (cl->state == MEI_FILE_CONNECTED || - cl->state == MEI_FILE_DISCONNECTED), - mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); + wait_event_timeout(dev->wait_recvd_msg, + (cl->state == MEI_FILE_CONNECTED || + cl->state == MEI_FILE_DISCONNECTED), + mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); mutex_lock(&dev->device_lock); if (cl->state != MEI_FILE_CONNECTED) { - rets = -EFAULT; + /* something went really wrong */ + if (!cl->status) + cl->status = -EFAULT; mei_io_list_flush(&dev->ctrl_rd_list, cl); mei_io_list_flush(&dev->ctrl_wr_list, cl); - goto out; } rets = cl->status; @@ -554,7 +612,8 @@ out: int mei_cl_flow_ctrl_creds(struct mei_cl *cl) { struct mei_device *dev; - int i; + struct mei_me_client *me_cl; + int id; if (WARN_ON(!cl || !cl->dev)) return -EINVAL; @@ -567,19 +626,19 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) if (cl->mei_flow_ctrl_creds > 0) return 1; - for (i = 0; i < dev->me_clients_num; i++) { - struct mei_me_client *me_cl = &dev->me_clients[i]; - if (me_cl->client_id == cl->me_client_id) { - if (me_cl->mei_flow_ctrl_creds) { - if (WARN_ON(me_cl->props.single_recv_buf == 0)) - return -EINVAL; - return 1; - } else { - return 0; - } - } + id = mei_me_cl_by_id(dev, cl->me_client_id); + if (id < 0) { + cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); + return id; } - return -ENOENT; + + me_cl = &dev->me_clients[id]; + if (me_cl->mei_flow_ctrl_creds) { + if (WARN_ON(me_cl->props.single_recv_buf == 0)) + return -EINVAL; + return 1; + } + return 0; } /** @@ -595,32 +654,31 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) { struct mei_device *dev; - int i; + struct mei_me_client *me_cl; + int id; if (WARN_ON(!cl || !cl->dev)) return -EINVAL; dev = cl->dev; - if (!dev->me_clients_num) - return -ENOENT; + id = mei_me_cl_by_id(dev, cl->me_client_id); + if (id < 0) { + cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); + return id; + } - for (i = 0; i < dev->me_clients_num; i++) { - struct mei_me_client *me_cl = &dev->me_clients[i]; - if (me_cl->client_id == cl->me_client_id) { - if (me_cl->props.single_recv_buf != 0) { - if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) - return -EINVAL; - dev->me_clients[i].mei_flow_ctrl_creds--; - } else { - if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) - return -EINVAL; - cl->mei_flow_ctrl_creds--; - } - return 0; - } + me_cl = &dev->me_clients[id]; + if (me_cl->props.single_recv_buf != 0) { + if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) + return -EINVAL; + me_cl->mei_flow_ctrl_creds--; + } else { + if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) + return -EINVAL; + cl->mei_flow_ctrl_creds--; } - return -ENOENT; + return 0; } /** @@ -652,7 +710,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) i = mei_me_cl_by_id(dev, cl->me_client_id); if (i < 0) { cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); - return -ENODEV; + return -ENOTTY; } cb = mei_io_cb_init(cl, NULL); @@ -666,8 +724,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length) goto err; cb->fop_type = MEI_FOP_READ; - if (dev->hbuf_is_ready) { - dev->hbuf_is_ready = false; + if (mei_hbuf_acquire(dev)) { if (mei_hbm_cl_flow_control_req(dev, cl)) { cl_err(dev, cl, "flow control send failed\n"); rets = -ENODEV; @@ -687,27 +744,26 @@ err: } /** - * mei_cl_irq_write_complete - write a message to device + * mei_cl_irq_write - write a message to device * from the interrupt thread context * * @cl: client * @cb: callback block. - * @slots: free slots. * @cmpl_list: complete list. * * returns 0, OK; otherwise error. */ -int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, - s32 *slots, struct mei_cl_cb *cmpl_list) +int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, + struct mei_cl_cb *cmpl_list) { struct mei_device *dev; struct mei_msg_data *buf; struct mei_msg_hdr mei_hdr; size_t len; u32 msg_slots; + int slots; int rets; - if (WARN_ON(!cl || !cl->dev)) return -ENODEV; @@ -724,6 +780,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, return 0; } + slots = mei_hbuf_empty_slots(dev); len = buf->size - cb->buf_idx; msg_slots = mei_data2slots(len); @@ -732,13 +789,13 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, mei_hdr.reserved = 0; mei_hdr.internal = cb->internal; - if (*slots >= msg_slots) { + if (slots >= msg_slots) { mei_hdr.length = len; mei_hdr.msg_complete = 1; /* Split the message only if we can write the whole host buffer */ - } else if (*slots == dev->hbuf_depth) { - msg_slots = *slots; - len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); + } else if (slots == dev->hbuf_depth) { + msg_slots = slots; + len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); mei_hdr.length = len; mei_hdr.msg_complete = 0; } else { @@ -749,7 +806,6 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", cb->request_buffer.size, cb->buf_idx); - *slots -= msg_slots; rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); if (rets) { cl->status = rets; @@ -802,21 +858,29 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) cb->fop_type = MEI_FOP_WRITE; + cb->buf_idx = 0; + cl->writing_state = MEI_IDLE; + + mei_hdr.host_addr = cl->host_client_id; + mei_hdr.me_addr = cl->me_client_id; + mei_hdr.reserved = 0; + mei_hdr.msg_complete = 0; + mei_hdr.internal = cb->internal; rets = mei_cl_flow_ctrl_creds(cl); if (rets < 0) goto err; - /* Host buffer is not ready, we queue the request */ - if (rets == 0 || !dev->hbuf_is_ready) { - cb->buf_idx = 0; - /* unseting complete will enqueue the cb for write */ - mei_hdr.msg_complete = 0; + if (rets == 0) { + cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); + rets = buf->size; + goto out; + } + if (!mei_hbuf_acquire(dev)) { + cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n"); rets = buf->size; goto out; } - - dev->hbuf_is_ready = false; /* Check for a maximum length */ if (buf->size > mei_hbuf_max_len(dev)) { @@ -827,12 +891,6 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) mei_hdr.msg_complete = 1; } - mei_hdr.host_addr = cl->host_client_id; - mei_hdr.me_addr = cl->me_client_id; - mei_hdr.reserved = 0; - mei_hdr.internal = cb->internal; - - rets = mei_write_message(dev, &mei_hdr, buf->data); if (rets) goto err; @@ -840,13 +898,12 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) cl->writing_state = MEI_WRITING; cb->buf_idx = mei_hdr.length; - rets = buf->size; out: if (mei_hdr.msg_complete) { - if (mei_cl_flow_ctrl_reduce(cl)) { - rets = -ENODEV; + rets = mei_cl_flow_ctrl_reduce(cl); + if (rets < 0) goto err; - } + list_add_tail(&cb->list, &dev->write_waiting_list.list); } else { list_add_tail(&cb->list, &dev->write_list.list); @@ -856,15 +913,18 @@ out: if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { mutex_unlock(&dev->device_lock); - if (wait_event_interruptible(cl->tx_wait, - cl->writing_state == MEI_WRITE_COMPLETE)) { - if (signal_pending(current)) - rets = -EINTR; - else - rets = -ERESTARTSYS; - } + rets = wait_event_interruptible(cl->tx_wait, + cl->writing_state == MEI_WRITE_COMPLETE); mutex_lock(&dev->device_lock); + /* wait_event_interruptible returns -ERESTARTSYS */ + if (rets) { + if (signal_pending(current)) + rets = -EINTR; + goto err; + } } + + rets = buf->size; err: return rets; } @@ -905,9 +965,9 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) void mei_cl_all_disconnect(struct mei_device *dev) { - struct mei_cl *cl, *next; + struct mei_cl *cl; - list_for_each_entry_safe(cl, next, &dev->file_list, link) { + list_for_each_entry(cl, &dev->file_list, link) { cl->state = MEI_FILE_DISCONNECTED; cl->mei_flow_ctrl_creds = 0; cl->timer_count = 0; @@ -922,8 +982,8 @@ void mei_cl_all_disconnect(struct mei_device *dev) */ void mei_cl_all_wakeup(struct mei_device *dev) { - struct mei_cl *cl, *next; - list_for_each_entry_safe(cl, next, &dev->file_list, link) { + struct mei_cl *cl; + list_for_each_entry(cl, &dev->file_list, link) { if (waitqueue_active(&cl->rx_wait)) { cl_dbg(dev, cl, "Waking up reading client!\n"); wake_up_interruptible(&cl->rx_wait); @@ -942,20 +1002,8 @@ void mei_cl_all_wakeup(struct mei_device *dev) */ void mei_cl_all_write_clear(struct mei_device *dev) { - struct mei_cl_cb *cb, *next; - struct list_head *list; - - list = &dev->write_list.list; - list_for_each_entry_safe(cb, next, list, list) { - list_del(&cb->list); - mei_io_cb_free(cb); - } - - list = &dev->write_waiting_list.list; - list_for_each_entry_safe(cb, next, list, list) { - list_del(&cb->list); - mei_io_cb_free(cb); - } + mei_io_list_free(&dev->write_list, NULL); + mei_io_list_free(&dev->write_waiting_list, NULL); } diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index c8396e582f1c..96d5de0389f9 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -45,8 +45,6 @@ static inline void mei_io_list_init(struct mei_cl_cb *list) { INIT_LIST_HEAD(&list->list); } -void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl); - /* * MEI Host Client Functions */ @@ -61,22 +59,6 @@ int mei_cl_unlink(struct mei_cl *cl); int mei_cl_flush_queues(struct mei_cl *cl); struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl); -/** - * mei_cl_cmp_id - tells if file private data have same id - * - * @fe1: private data of 1. file object - * @fe2: private data of 2. file object - * - * returns true - if ids are the same and not NULL - */ -static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, - const struct mei_cl *cl2) -{ - return cl1 && cl2 && - (cl1->host_client_id == cl2->host_client_id) && - (cl1->me_client_id == cl2->me_client_id); -} - int mei_cl_flow_ctrl_creds(struct mei_cl *cl); @@ -86,15 +68,15 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl); */ static inline bool mei_cl_is_connected(struct mei_cl *cl) { - return (cl->dev && + return cl->dev && cl->dev->dev_state == MEI_DEV_ENABLED && - cl->state == MEI_FILE_CONNECTED); + cl->state == MEI_FILE_CONNECTED; } static inline bool mei_cl_is_transitioning(struct mei_cl *cl) { - return (MEI_FILE_INITIALIZING == cl->state || + return MEI_FILE_INITIALIZING == cl->state || MEI_FILE_DISCONNECTED == cl->state || - MEI_FILE_DISCONNECTING == cl->state); + MEI_FILE_DISCONNECTING == cl->state; } bool mei_cl_is_other_connecting(struct mei_cl *cl); @@ -102,8 +84,8 @@ int mei_cl_disconnect(struct mei_cl *cl); int mei_cl_connect(struct mei_cl *cl, struct file *file); int mei_cl_read_start(struct mei_cl *cl, size_t length); int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking); -int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, - s32 *slots, struct mei_cl_cb *cmpl_list); +int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, + struct mei_cl_cb *cmpl_list); void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb); diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index a3ae154444b2..ced5b777c70f 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -75,6 +75,54 @@ static const struct file_operations mei_dbgfs_fops_meclients = { .llseek = generic_file_llseek, }; +static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_device *dev = fp->private_data; + struct mei_cl *cl; + const size_t bufsz = 1024; + char *buf; + int i = 0; + int pos = 0; + int ret; + + if (!dev) + return -ENODEV; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, + " |me|host|state|rd|wr|\n"); + + mutex_lock(&dev->device_lock); + + /* if the driver is not enabled the list won't b consitent */ + if (dev->dev_state != MEI_DEV_ENABLED) + goto out; + + list_for_each_entry(cl, &dev->file_list, link) { + + pos += scnprintf(buf + pos, bufsz - pos, + "%2d|%2d|%4d|%5d|%2d|%2d|\n", + i, cl->me_client_id, cl->host_client_id, cl->state, + cl->reading_state, cl->writing_state); + i++; + } +out: + mutex_unlock(&dev->device_lock); + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} + +static const struct file_operations mei_dbgfs_fops_active = { + .open = simple_open, + .read = mei_dbgfs_read_active, + .llseek = generic_file_llseek, +}; + static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, size_t cnt, loff_t *ppos) { @@ -128,6 +176,12 @@ int mei_dbgfs_register(struct mei_device *dev, const char *name) dev_err(&dev->pdev->dev, "meclients: registration failed\n"); goto err; } + f = debugfs_create_file("active", S_IRUSR, dir, + dev, &mei_dbgfs_fops_active); + if (!f) { + dev_err(&dev->pdev->dev, "meclients: registration failed\n"); + goto err; + } f = debugfs_create_file("devstate", S_IRUSR, dir, dev, &mei_dbgfs_fops_devstate); if (!f) { diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 28cd74c073b9..4960288e543a 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -21,7 +21,41 @@ #include "mei_dev.h" #include "hbm.h" -#include "hw-me.h" +#include "client.h" + +static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status) +{ +#define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status + switch (status) { + MEI_CL_CS(SUCCESS); + MEI_CL_CS(NOT_FOUND); + MEI_CL_CS(ALREADY_STARTED); + MEI_CL_CS(OUT_OF_RESOURCES); + MEI_CL_CS(MESSAGE_SMALL); + default: return "unknown"; + } +#undef MEI_CL_CCS +} + +/** + * mei_cl_conn_status_to_errno - convert client connect response + * status to error code + * + * @status: client connect response status + * + * returns corresponding error code + */ +static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status) +{ + switch (status) { + case MEI_CL_CONN_SUCCESS: return 0; + case MEI_CL_CONN_NOT_FOUND: return -ENOTTY; + case MEI_CL_CONN_ALREADY_STARTED: return -EBUSY; + case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY; + case MEI_CL_CONN_MESSAGE_SMALL: return -EINVAL; + default: return -EINVAL; + } +} /** * mei_hbm_me_cl_allocate - allocates storage for me clients @@ -100,33 +134,6 @@ bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf) /** - * is_treat_specially_client - checks if the message belongs - * to the file private data. - * - * @cl: private data of the file object - * @rs: connect response bus message - * - */ -static bool is_treat_specially_client(struct mei_cl *cl, - struct hbm_client_connect_response *rs) -{ - if (mei_hbm_cl_addr_equal(cl, rs)) { - if (!rs->status) { - cl->state = MEI_FILE_CONNECTED; - cl->status = 0; - - } else { - cl->state = MEI_FILE_DISCONNECTED; - cl->status = -ENODEV; - } - cl->timer_count = 0; - - return true; - } - return false; -} - -/** * mei_hbm_idle - set hbm to idle state * * @dev: the device structure @@ -147,13 +154,13 @@ int mei_hbm_start_wait(struct mei_device *dev) ret = wait_event_interruptible_timeout(dev->wait_recvd_msg, dev->hbm_state == MEI_HBM_IDLE || dev->hbm_state >= MEI_HBM_STARTED, - mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); + mei_secs_to_jiffies(MEI_HBM_TIMEOUT)); mutex_lock(&dev->device_lock); if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) { dev->hbm_state = MEI_HBM_IDLE; dev_err(&dev->pdev->dev, "waiting for mei start failed\n"); - return -ETIMEDOUT; + return -ETIME; } return 0; } @@ -283,17 +290,18 @@ static int mei_hbm_prop_req(struct mei_device *dev) } /** - * mei_hbm_stop_req_prepare - prepare stop request message + * mei_hbm_stop_req - send stop request message * * @dev - mei device - * @mei_hdr - mei message header - * @data - hbm message body buffer + * @cl: client info + * + * This function returns -EIO on write failure */ -static void mei_hbm_stop_req_prepare(struct mei_device *dev, - struct mei_msg_hdr *mei_hdr, unsigned char *data) +static int mei_hbm_stop_req(struct mei_device *dev) { + struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; struct hbm_host_stop_request *req = - (struct hbm_host_stop_request *)data; + (struct hbm_host_stop_request *)dev->wr_msg.data; const size_t len = sizeof(struct hbm_host_stop_request); mei_hbm_hdr(mei_hdr, len); @@ -301,6 +309,8 @@ static void mei_hbm_stop_req_prepare(struct mei_device *dev, memset(req, 0, len); req->hbm_cmd = HOST_STOP_REQ_CMD; req->reason = DRIVER_STOP_REQUEST; + + return mei_write_message(dev, mei_hdr, dev->wr_msg.data); } /** @@ -319,8 +329,7 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl) mei_hbm_hdr(mei_hdr, len); mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len); - dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n", - cl->host_client_id, cl->me_client_id); + cl_dbg(dev, cl, "sending flow control\n"); return mei_write_message(dev, mei_hdr, dev->wr_msg.data); } @@ -330,27 +339,34 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl) * * @dev: the device structure * @flow: flow control. + * + * return 0 on success, < 0 otherwise */ -static void mei_hbm_add_single_flow_creds(struct mei_device *dev, +static int mei_hbm_add_single_flow_creds(struct mei_device *dev, struct hbm_flow_control *flow) { - struct mei_me_client *client; - int i; - - for (i = 0; i < dev->me_clients_num; i++) { - client = &dev->me_clients[i]; - if (client && flow->me_addr == client->client_id) { - if (client->props.single_recv_buf) { - client->mei_flow_ctrl_creds++; - dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n", - flow->me_addr); - dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n", - client->mei_flow_ctrl_creds); - } else { - BUG(); /* error in flow control */ - } - } + struct mei_me_client *me_cl; + int id; + + id = mei_me_cl_by_id(dev, flow->me_addr); + if (id < 0) { + dev_err(&dev->pdev->dev, "no such me client %d\n", + flow->me_addr); + return id; } + + me_cl = &dev->me_clients[id]; + if (me_cl->props.single_recv_buf) { + me_cl->mei_flow_ctrl_creds++; + dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n", + flow->me_addr); + dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n", + me_cl->mei_flow_ctrl_creds); + } else { + BUG(); /* error in flow control */ + } + + return 0; } /** @@ -362,8 +378,7 @@ static void mei_hbm_add_single_flow_creds(struct mei_device *dev, static void mei_hbm_cl_flow_control_res(struct mei_device *dev, struct hbm_flow_control *flow_control) { - struct mei_cl *cl = NULL; - struct mei_cl *next = NULL; + struct mei_cl *cl; if (!flow_control->host_addr) { /* single receive buffer */ @@ -372,7 +387,7 @@ static void mei_hbm_cl_flow_control_res(struct mei_device *dev, } /* normal connection */ - list_for_each_entry_safe(cl, next, &dev->file_list, link) { + list_for_each_entry(cl, &dev->file_list, link) { if (mei_hbm_cl_addr_equal(cl, flow_control)) { cl->mei_flow_ctrl_creds++; dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n", @@ -405,6 +420,25 @@ int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl) } /** + * mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW + * + * @dev: the device structure + * @cl: a client to disconnect from + * + * This function returns -EIO on write failure + */ +int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl) +{ + struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; + const size_t len = sizeof(struct hbm_client_connect_response); + + mei_hbm_hdr(mei_hdr, len); + mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, dev->wr_msg.data, len); + + return mei_write_message(dev, mei_hdr, dev->wr_msg.data); +} + +/** * mei_hbm_cl_disconnect_res - disconnect response from ME * * @dev: the device structure @@ -414,29 +448,23 @@ static void mei_hbm_cl_disconnect_res(struct mei_device *dev, struct hbm_client_connect_response *rs) { struct mei_cl *cl; - struct mei_cl_cb *pos = NULL, *next = NULL; - - dev_dbg(&dev->pdev->dev, - "disconnect_response:\n" - "ME Client = %d\n" - "Host Client = %d\n" - "Status = %d\n", - rs->me_addr, - rs->host_addr, - rs->status); - - list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) { - cl = pos->cl; - - if (!cl) { - list_del(&pos->list); + struct mei_cl_cb *cb, *next; + + dev_dbg(&dev->pdev->dev, "hbm: disconnect response cl:host=%02d me=%02d status=%d\n", + rs->me_addr, rs->host_addr, rs->status); + + list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) { + cl = cb->cl; + + /* this should not happen */ + if (WARN_ON(!cl)) { + list_del(&cb->list); return; } - dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n"); if (mei_hbm_cl_addr_equal(cl, rs)) { - list_del(&pos->list); - if (!rs->status) + list_del(&cb->list); + if (rs->status == MEI_CL_DISCONN_SUCCESS) cl->state = MEI_FILE_DISCONNECTED; cl->status = 0; @@ -476,46 +504,41 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, { struct mei_cl *cl; - struct mei_cl_cb *pos = NULL, *next = NULL; + struct mei_cl_cb *cb, *next; - dev_dbg(&dev->pdev->dev, - "connect_response:\n" - "ME Client = %d\n" - "Host Client = %d\n" - "Status = %d\n", - rs->me_addr, - rs->host_addr, - rs->status); + dev_dbg(&dev->pdev->dev, "hbm: connect response cl:host=%02d me=%02d status=%s\n", + rs->me_addr, rs->host_addr, + mei_cl_conn_status_str(rs->status)); - /* if WD or iamthif client treat specially */ + cl = NULL; - if (is_treat_specially_client(&dev->wd_cl, rs)) { - dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n"); - mei_watchdog_register(dev); + list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) { - return; - } + cl = cb->cl; + /* this should not happen */ + if (WARN_ON(!cl)) { + list_del_init(&cb->list); + continue; + } - if (is_treat_specially_client(&dev->iamthif_cl, rs)) { - dev->iamthif_state = MEI_IAMTHIF_IDLE; - return; - } - list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) { + if (cb->fop_type != MEI_FOP_CONNECT) + continue; - cl = pos->cl; - if (!cl) { - list_del(&pos->list); - return; - } - if (pos->fop_type == MEI_FOP_IOCTL) { - if (is_treat_specially_client(cl, rs)) { - list_del(&pos->list); - cl->status = 0; - cl->timer_count = 0; - break; - } + if (mei_hbm_cl_addr_equal(cl, rs)) { + list_del(&cb->list); + break; } } + + if (!cl) + return; + + cl->timer_count = 0; + if (rs->status == MEI_CL_CONN_SUCCESS) + cl->state = MEI_FILE_CONNECTED; + else + cl->state = MEI_FILE_DISCONNECTED; + cl->status = mei_cl_conn_status_to_errno(rs->status); } @@ -525,32 +548,34 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, * * @dev: the device structure. * @disconnect_req: disconnect request bus message from the me + * + * returns -ENOMEM on allocation failure */ -static void mei_hbm_fw_disconnect_req(struct mei_device *dev, +static int mei_hbm_fw_disconnect_req(struct mei_device *dev, struct hbm_client_connect_request *disconnect_req) { - struct mei_cl *cl, *next; - const size_t len = sizeof(struct hbm_client_connect_response); + struct mei_cl *cl; + struct mei_cl_cb *cb; - list_for_each_entry_safe(cl, next, &dev->file_list, link) { + list_for_each_entry(cl, &dev->file_list, link) { if (mei_hbm_cl_addr_equal(cl, disconnect_req)) { dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n", disconnect_req->host_addr, disconnect_req->me_addr); cl->state = MEI_FILE_DISCONNECTED; cl->timer_count = 0; - if (cl == &dev->wd_cl) - dev->wd_pending = false; - else if (cl == &dev->iamthif_cl) - dev->iamthif_timer = 0; - - /* prepare disconnect response */ - mei_hbm_hdr(&dev->wr_ext_msg.hdr, len); - mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, - dev->wr_ext_msg.data, len); + + cb = mei_io_cb_init(cl, NULL); + if (!cb) + return -ENOMEM; + cb->fop_type = MEI_FOP_DISCONNECT_RSP; + cl_dbg(dev, cl, "add disconnect response as first\n"); + list_add(&cb->list, &dev->ctrl_wr_list.list); + break; } } + return 0; } @@ -629,10 +654,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n"); dev->hbm_state = MEI_HBM_STOPPED; - mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr, - dev->wr_msg.data); - if (mei_write_message(dev, &dev->wr_msg.hdr, - dev->wr_msg.data)) { + if (mei_hbm_stop_req(dev)) { dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n"); return -EIO; } @@ -778,10 +800,11 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) case ME_STOP_REQ_CMD: dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n"); - dev->hbm_state = MEI_HBM_STOPPED; - mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr, - dev->wr_ext_msg.data); + if (mei_hbm_stop_req(dev)) { + dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n"); + return -EIO; + } break; default: BUG(); diff --git a/drivers/misc/mei/hbm.h b/drivers/misc/mei/hbm.h index 5f92188a5cd7..20e8782711c0 100644 --- a/drivers/misc/mei/hbm.h +++ b/drivers/misc/mei/hbm.h @@ -54,6 +54,7 @@ int mei_hbm_start_req(struct mei_device *dev); int mei_hbm_start_wait(struct mei_device *dev); int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl); int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl); +int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl); int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl); bool mei_hbm_version_is_supported(struct mei_device *dev); diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 6f656c053b14..8dbdaaef1af5 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -20,10 +20,10 @@ #include <linux/interrupt.h> #include "mei_dev.h" -#include "hw-me.h" - #include "hbm.h" +#include "hw-me.h" +#include "hw-me-regs.h" /** * mei_me_reg_read - Reads 32bit data from the mei device @@ -240,11 +240,11 @@ static int mei_me_hw_ready_wait(struct mei_device *dev) mutex_unlock(&dev->device_lock); err = wait_event_interruptible_timeout(dev->wait_hw_ready, dev->recvd_hw_ready, - mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); + mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT)); mutex_lock(&dev->device_lock); if (!err && !dev->recvd_hw_ready) { if (!err) - err = -ETIMEDOUT; + err = -ETIME; dev_err(&dev->pdev->dev, "wait hw ready failed. status = %d\n", err); return err; @@ -303,7 +303,7 @@ static bool mei_me_hbuf_is_empty(struct mei_device *dev) * * @dev: the device structure * - * returns -1(ESLOTS_OVERFLOW) if overflow, otherwise empty slots count + * returns -EOVERFLOW if overflow, otherwise empty slots count */ static int mei_me_hbuf_empty_slots(struct mei_device *dev) { @@ -326,7 +326,7 @@ static size_t mei_me_hbuf_max_len(const struct mei_device *dev) /** - * mei_write_message - writes a message to mei device. + * mei_me_write_message - writes a message to mei device. * * @dev: the device structure * @header: mei HECI header of message @@ -354,7 +354,7 @@ static int mei_me_write_message(struct mei_device *dev, dw_cnt = mei_data2slots(length); if (empty_slots < 0 || dw_cnt > empty_slots) - return -EIO; + return -EMSGSIZE; mei_me_reg_write(hw, H_CB_WW, *((u32 *) header)); @@ -381,7 +381,7 @@ static int mei_me_write_message(struct mei_device *dev, * * @dev: the device structure * - * returns -1(ESLOTS_OVERFLOW) if overflow, otherwise filled slots count + * returns -EOVERFLOW if overflow, otherwise filled slots count */ static int mei_me_count_full_read_slots(struct mei_device *dev) { @@ -505,17 +505,25 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) /* check slots available for reading */ slots = mei_count_full_read_slots(dev); while (slots > 0) { - /* we have urgent data to send so break the read */ - if (dev->wr_ext_msg.hdr.length) - break; dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots); rets = mei_irq_read_handler(dev, &complete_list, &slots); + /* There is a race between ME write and interrupt delivery: + * Not all data is always available immediately after the + * interrupt, so try to read again on the next interrupt. + */ + if (rets == -ENODATA) + break; + if (rets && dev->dev_state != MEI_DEV_RESETTING) { + dev_err(&dev->pdev->dev, "mei_irq_read_handler ret = %d.\n", + rets); schedule_work(&dev->reset_work); goto end; } } + dev->hbuf_is_ready = mei_hbuf_is_ready(dev); + rets = mei_irq_write_handler(dev, &complete_list); dev->hbuf_is_ready = mei_hbuf_is_ready(dev); diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 80bd829fbd9a..893d5119fa9b 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -20,6 +20,7 @@ #define _MEI_INTERFACE_H_ #include <linux/mei.h> +#include <linux/irqreturn.h> #include "mei_dev.h" #include "client.h" diff --git a/drivers/misc/mei/hw-txe-regs.h b/drivers/misc/mei/hw-txe-regs.h new file mode 100644 index 000000000000..7283c24c1af1 --- /dev/null +++ b/drivers/misc/mei/hw-txe-regs.h @@ -0,0 +1,294 @@ +/****************************************************************************** + * Intel Management Engine Interface (Intel MEI) Linux driver + * Intel MEI Interface Header + * + * 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) 2013 - 2014 Intel Corporation. All rights reserved. + * + * 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. + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING + * + * Contact Information: + * Intel Corporation. + * linux-mei@linux.intel.com + * http://www.intel.com + * + * BSD LICENSE + * + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. + * 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 _MEI_HW_TXE_REGS_H_ +#define _MEI_HW_TXE_REGS_H_ + +#include "hw.h" + +#define SEC_ALIVENESS_TIMER_TIMEOUT (5 * MSEC_PER_SEC) +#define SEC_ALIVENESS_WAIT_TIMEOUT (1 * MSEC_PER_SEC) +#define SEC_RESET_WAIT_TIMEOUT (1 * MSEC_PER_SEC) +#define SEC_READY_WAIT_TIMEOUT (5 * MSEC_PER_SEC) +#define START_MESSAGE_RESPONSE_WAIT_TIMEOUT (5 * MSEC_PER_SEC) +#define RESET_CANCEL_WAIT_TIMEOUT (1 * MSEC_PER_SEC) + +enum { + SEC_BAR, + BRIDGE_BAR, + + NUM_OF_MEM_BARS +}; + +/* SeC FW Status Register + * + * FW uses this register in order to report its status to host. + * This register resides in PCI-E config space. + */ +#define PCI_CFG_TXE_FW_STS0 0x40 +# define PCI_CFG_TXE_FW_STS0_WRK_ST_MSK 0x0000000F +# define PCI_CFG_TXE_FW_STS0_OP_ST_MSK 0x000001C0 +# define PCI_CFG_TXE_FW_STS0_FW_INIT_CMPLT 0x00000200 +# define PCI_CFG_TXE_FW_STS0_ERR_CODE_MSK 0x0000F000 +# define PCI_CFG_TXE_FW_STS0_OP_MODE_MSK 0x000F0000 +# define PCI_CFG_TXE_FW_STS0_RST_CNT_MSK 0x00F00000 + + +#define IPC_BASE_ADDR 0x80400 /* SeC IPC Base Address */ + +/* IPC Input Doorbell Register */ +#define SEC_IPC_INPUT_DOORBELL_REG (0x0000 + IPC_BASE_ADDR) + +/* IPC Input Status Register + * This register indicates whether or not processing of + * the most recent command has been completed by the SEC + * New commands and payloads should not be written by the Host + * until this indicates that the previous command has been processed. + */ +#define SEC_IPC_INPUT_STATUS_REG (0x0008 + IPC_BASE_ADDR) +# define SEC_IPC_INPUT_STATUS_RDY BIT(0) + +/* IPC Host Interrupt Status Register */ +#define SEC_IPC_HOST_INT_STATUS_REG (0x0010 + IPC_BASE_ADDR) +#define SEC_IPC_HOST_INT_STATUS_OUT_DB BIT(0) +#define SEC_IPC_HOST_INT_STATUS_IN_RDY BIT(1) +#define SEC_IPC_HOST_INT_STATUS_HDCP_M0_RCVD BIT(5) +#define SEC_IPC_HOST_INT_STATUS_ILL_MEM_ACCESS BIT(17) +#define SEC_IPC_HOST_INT_STATUS_AES_HKEY_ERR BIT(18) +#define SEC_IPC_HOST_INT_STATUS_DES_HKEY_ERR BIT(19) +#define SEC_IPC_HOST_INT_STATUS_TMRMTB_OVERFLOW BIT(21) + +/* Convenient mask for pending interrupts */ +#define SEC_IPC_HOST_INT_STATUS_PENDING \ + (SEC_IPC_HOST_INT_STATUS_OUT_DB| \ + SEC_IPC_HOST_INT_STATUS_IN_RDY) + +/* IPC Host Interrupt Mask Register */ +#define SEC_IPC_HOST_INT_MASK_REG (0x0014 + IPC_BASE_ADDR) + +# define SEC_IPC_HOST_INT_MASK_OUT_DB BIT(0) /* Output Doorbell Int Mask */ +# define SEC_IPC_HOST_INT_MASK_IN_RDY BIT(1) /* Input Ready Int Mask */ + +/* IPC Input Payload RAM */ +#define SEC_IPC_INPUT_PAYLOAD_REG (0x0100 + IPC_BASE_ADDR) +/* IPC Shared Payload RAM */ +#define IPC_SHARED_PAYLOAD_REG (0x0200 + IPC_BASE_ADDR) + +/* SeC Address Translation Table Entry 2 - Ctrl + * + * This register resides also in SeC's PCI-E Memory space. + */ +#define SATT2_CTRL_REG 0x1040 +# define SATT2_CTRL_VALID_MSK BIT(0) +# define SATT2_CTRL_BR_BASE_ADDR_REG_SHIFT 8 +# define SATT2_CTRL_BRIDGE_HOST_EN_MSK BIT(12) + +/* SATT Table Entry 2 SAP Base Address Register */ +#define SATT2_SAP_BA_REG 0x1044 +/* SATT Table Entry 2 SAP Size Register. */ +#define SATT2_SAP_SIZE_REG 0x1048 + /* SATT Table Entry 2 SAP Bridge Address - LSB Register */ +#define SATT2_BRG_BA_LSB_REG 0x104C + +/* Host High-level Interrupt Status Register */ +#define HHISR_REG 0x2020 +/* Host High-level Interrupt Enable Register + * + * Resides in PCI memory space. This is the top hierarchy for + * interrupts from SeC to host, aggregating both interrupts that + * arrive through HICR registers as well as interrupts + * that arrive via IPC. + */ +#define HHIER_REG 0x2024 +#define IPC_HHIER_SEC BIT(0) +#define IPC_HHIER_BRIDGE BIT(1) +#define IPC_HHIER_MSK (IPC_HHIER_SEC | IPC_HHIER_BRIDGE) + +/* Host High-level Interrupt Mask Register. + * + * Resides in PCI memory space. + * This is the top hierarchy for masking interrupts from SeC to host. + */ +#define HHIMR_REG 0x2028 +#define IPC_HHIMR_SEC BIT(0) +#define IPC_HHIMR_BRIDGE BIT(1) + +/* Host High-level IRQ Status Register */ +#define HHIRQSR_REG 0x202C + +/* Host Interrupt Cause Register 0 - SeC IPC Readiness + * + * This register is both an ICR to Host from PCI Memory Space + * and it is also exposed in the SeC memory space. + * This register is used by SeC's IPC driver in order + * to synchronize with host about IPC interface state. + */ +#define HICR_SEC_IPC_READINESS_REG 0x2040 +#define HICR_SEC_IPC_READINESS_HOST_RDY BIT(0) +#define HICR_SEC_IPC_READINESS_SEC_RDY BIT(1) +#define HICR_SEC_IPC_READINESS_SYS_RDY \ + (HICR_SEC_IPC_READINESS_HOST_RDY | \ + HICR_SEC_IPC_READINESS_SEC_RDY) +#define HICR_SEC_IPC_READINESS_RDY_CLR BIT(2) + +/* Host Interrupt Cause Register 1 - Aliveness Response */ +/* This register is both an ICR to Host from PCI Memory Space + * and it is also exposed in the SeC memory space. + * The register may be used by SeC to ACK a host request for aliveness. + */ +#define HICR_HOST_ALIVENESS_RESP_REG 0x2044 +#define HICR_HOST_ALIVENESS_RESP_ACK BIT(0) + +/* Host Interrupt Cause Register 2 - SeC IPC Output Doorbell */ +#define HICR_SEC_IPC_OUTPUT_DOORBELL_REG 0x2048 + +/* Host Interrupt Status Register. + * + * Resides in PCI memory space. + * This is the main register involved in generating interrupts + * from SeC to host via HICRs. + * The interrupt generation rules are as follows: + * An interrupt will be generated whenever for any i, + * there is a transition from a state where at least one of + * the following conditions did not hold, to a state where + * ALL the following conditions hold: + * A) HISR.INT[i]_STS == 1. + * B) HIER.INT[i]_EN == 1. + */ +#define HISR_REG 0x2060 +#define HISR_INT_0_STS BIT(0) +#define HISR_INT_1_STS BIT(1) +#define HISR_INT_2_STS BIT(2) +#define HISR_INT_3_STS BIT(3) +#define HISR_INT_4_STS BIT(4) +#define HISR_INT_5_STS BIT(5) +#define HISR_INT_6_STS BIT(6) +#define HISR_INT_7_STS BIT(7) +#define HISR_INT_STS_MSK \ + (HISR_INT_0_STS | HISR_INT_1_STS | HISR_INT_2_STS) + +/* Host Interrupt Enable Register. Resides in PCI memory space. */ +#define HIER_REG 0x2064 +#define HIER_INT_0_EN BIT(0) +#define HIER_INT_1_EN BIT(1) +#define HIER_INT_2_EN BIT(2) +#define HIER_INT_3_EN BIT(3) +#define HIER_INT_4_EN BIT(4) +#define HIER_INT_5_EN BIT(5) +#define HIER_INT_6_EN BIT(6) +#define HIER_INT_7_EN BIT(7) + +#define HIER_INT_EN_MSK \ + (HIER_INT_0_EN | HIER_INT_1_EN | HIER_INT_2_EN) + + +/* SEC Memory Space IPC output payload. + * + * This register is part of the output payload which SEC provides to host. + */ +#define BRIDGE_IPC_OUTPUT_PAYLOAD_REG 0x20C0 + +/* SeC Interrupt Cause Register - Host Aliveness Request + * This register is both an ICR to SeC and it is also exposed + * in the host-visible PCI memory space. + * The register is used by host to request SeC aliveness. + */ +#define SICR_HOST_ALIVENESS_REQ_REG 0x214C +#define SICR_HOST_ALIVENESS_REQ_REQUESTED BIT(0) + + +/* SeC Interrupt Cause Register - Host IPC Readiness + * + * This register is both an ICR to SeC and it is also exposed + * in the host-visible PCI memory space. + * This register is used by the host's SeC driver uses in order + * to synchronize with SeC about IPC interface state. + */ +#define SICR_HOST_IPC_READINESS_REQ_REG 0x2150 + + +#define SICR_HOST_IPC_READINESS_HOST_RDY BIT(0) +#define SICR_HOST_IPC_READINESS_SEC_RDY BIT(1) +#define SICR_HOST_IPC_READINESS_SYS_RDY \ + (SICR_HOST_IPC_READINESS_HOST_RDY | \ + SICR_HOST_IPC_READINESS_SEC_RDY) +#define SICR_HOST_IPC_READINESS_RDY_CLR BIT(2) + +/* SeC Interrupt Cause Register - SeC IPC Output Status + * + * This register indicates whether or not processing of the most recent + * command has been completed by the Host. + * New commands and payloads should not be written by SeC until this + * register indicates that the previous command has been processed. + */ +#define SICR_SEC_IPC_OUTPUT_STATUS_REG 0x2154 +# define SEC_IPC_OUTPUT_STATUS_RDY BIT(0) + + + +/* MEI IPC Message payload size 64 bytes */ +#define PAYLOAD_SIZE 64 + +/* MAX size for SATT range 32MB */ +#define SATT_RANGE_MAX (32 << 20) + + +#endif /* _MEI_HW_TXE_REGS_H_ */ + diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c new file mode 100644 index 000000000000..f60182a52f96 --- /dev/null +++ b/drivers/misc/mei/hw-txe.c @@ -0,0 +1,1107 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2013-2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include <linux/pci.h> +#include <linux/jiffies.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/irqreturn.h> + +#include <linux/mei.h> + +#include "mei_dev.h" +#include "hw-txe.h" +#include "client.h" +#include "hbm.h" + +/** + * mei_txe_reg_read - Reads 32bit data from the device + * + * @base_addr: registers base address + * @offset: register offset + * + */ +static inline u32 mei_txe_reg_read(void __iomem *base_addr, + unsigned long offset) +{ + return ioread32(base_addr + offset); +} + +/** + * mei_txe_reg_write - Writes 32bit data to the device + * + * @base_addr: registers base address + * @offset: register offset + * @value: the value to write + */ +static inline void mei_txe_reg_write(void __iomem *base_addr, + unsigned long offset, u32 value) +{ + iowrite32(value, base_addr + offset); +} + +/** + * mei_txe_sec_reg_read_silent - Reads 32bit data from the SeC BAR + * + * @dev: the device structure + * @offset: register offset + * + * Doesn't check for aliveness while Reads 32bit data from the SeC BAR + */ +static inline u32 mei_txe_sec_reg_read_silent(struct mei_txe_hw *hw, + unsigned long offset) +{ + return mei_txe_reg_read(hw->mem_addr[SEC_BAR], offset); +} + +/** + * mei_txe_sec_reg_read - Reads 32bit data from the SeC BAR + * + * @dev: the device structure + * @offset: register offset + * + * Reads 32bit data from the SeC BAR and shout loud if aliveness is not set + */ +static inline u32 mei_txe_sec_reg_read(struct mei_txe_hw *hw, + unsigned long offset) +{ + WARN(!hw->aliveness, "sec read: aliveness not asserted\n"); + return mei_txe_sec_reg_read_silent(hw, offset); +} +/** + * mei_txe_sec_reg_write_silent - Writes 32bit data to the SeC BAR + * doesn't check for aliveness + * + * @dev: the device structure + * @offset: register offset + * @value: value to write + * + * Doesn't check for aliveness while writes 32bit data from to the SeC BAR + */ +static inline void mei_txe_sec_reg_write_silent(struct mei_txe_hw *hw, + unsigned long offset, u32 value) +{ + mei_txe_reg_write(hw->mem_addr[SEC_BAR], offset, value); +} + +/** + * mei_txe_sec_reg_write - Writes 32bit data to the SeC BAR + * + * @dev: the device structure + * @offset: register offset + * @value: value to write + * + * Writes 32bit data from the SeC BAR and shout loud if aliveness is not set + */ +static inline void mei_txe_sec_reg_write(struct mei_txe_hw *hw, + unsigned long offset, u32 value) +{ + WARN(!hw->aliveness, "sec write: aliveness not asserted\n"); + mei_txe_sec_reg_write_silent(hw, offset, value); +} +/** + * mei_txe_br_reg_read - Reads 32bit data from the Bridge BAR + * + * @hw: the device structure + * @offset: offset from which to read the data + * + */ +static inline u32 mei_txe_br_reg_read(struct mei_txe_hw *hw, + unsigned long offset) +{ + return mei_txe_reg_read(hw->mem_addr[BRIDGE_BAR], offset); +} + +/** + * mei_txe_br_reg_write - Writes 32bit data to the Bridge BAR + * + * @hw: the device structure + * @offset: offset from which to write the data + * @value: the byte to write + */ +static inline void mei_txe_br_reg_write(struct mei_txe_hw *hw, + unsigned long offset, u32 value) +{ + mei_txe_reg_write(hw->mem_addr[BRIDGE_BAR], offset, value); +} + +/** + * mei_txe_aliveness_set - request for aliveness change + * + * @dev: the device structure + * @req: requested aliveness value + * + * Request for aliveness change and returns true if the change is + * really needed and false if aliveness is already + * in the requested state + * Requires device lock to be held + */ +static bool mei_txe_aliveness_set(struct mei_device *dev, u32 req) +{ + + struct mei_txe_hw *hw = to_txe_hw(dev); + bool do_req = hw->aliveness != req; + + dev_dbg(&dev->pdev->dev, "Aliveness current=%d request=%d\n", + hw->aliveness, req); + if (do_req) { + hw->recvd_aliveness = false; + mei_txe_br_reg_write(hw, SICR_HOST_ALIVENESS_REQ_REG, req); + } + return do_req; +} + + +/** + * mei_txe_aliveness_req_get - get aliveness requested register value + * + * @dev: the device structure + * + * Extract HICR_HOST_ALIVENESS_RESP_ACK bit from + * from HICR_HOST_ALIVENESS_REQ register value + */ +static u32 mei_txe_aliveness_req_get(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + u32 reg; + reg = mei_txe_br_reg_read(hw, SICR_HOST_ALIVENESS_REQ_REG); + return reg & SICR_HOST_ALIVENESS_REQ_REQUESTED; +} + +/** + * mei_txe_aliveness_get - get aliveness response register value + * @dev: the device structure + * + * Extract HICR_HOST_ALIVENESS_RESP_ACK bit + * from HICR_HOST_ALIVENESS_RESP register value + */ +static u32 mei_txe_aliveness_get(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + u32 reg; + reg = mei_txe_br_reg_read(hw, HICR_HOST_ALIVENESS_RESP_REG); + return reg & HICR_HOST_ALIVENESS_RESP_ACK; +} + +/** + * mei_txe_aliveness_poll - waits for aliveness to settle + * + * @dev: the device structure + * @expected: expected aliveness value + * + * Polls for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set + * returns > 0 if the expected value was received, -ETIME otherwise + */ +static int mei_txe_aliveness_poll(struct mei_device *dev, u32 expected) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + int t = 0; + + do { + hw->aliveness = mei_txe_aliveness_get(dev); + if (hw->aliveness == expected) { + dev_dbg(&dev->pdev->dev, + "aliveness settled after %d msecs\n", t); + return t; + } + mutex_unlock(&dev->device_lock); + msleep(MSEC_PER_SEC / 5); + mutex_lock(&dev->device_lock); + t += MSEC_PER_SEC / 5; + } while (t < SEC_ALIVENESS_WAIT_TIMEOUT); + + dev_err(&dev->pdev->dev, "aliveness timed out\n"); + return -ETIME; +} + +/** + * mei_txe_aliveness_wait - waits for aliveness to settle + * + * @dev: the device structure + * @expected: expected aliveness value + * + * Waits for HICR_HOST_ALIVENESS_RESP.ALIVENESS_RESP to be set + * returns returns 0 on success and < 0 otherwise + */ +static int mei_txe_aliveness_wait(struct mei_device *dev, u32 expected) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + const unsigned long timeout = + msecs_to_jiffies(SEC_ALIVENESS_WAIT_TIMEOUT); + long err; + int ret; + + hw->aliveness = mei_txe_aliveness_get(dev); + if (hw->aliveness == expected) + return 0; + + mutex_unlock(&dev->device_lock); + err = wait_event_timeout(hw->wait_aliveness, + hw->recvd_aliveness, timeout); + mutex_lock(&dev->device_lock); + + hw->aliveness = mei_txe_aliveness_get(dev); + ret = hw->aliveness == expected ? 0 : -ETIME; + + if (ret) + dev_err(&dev->pdev->dev, "aliveness timed out"); + else + dev_dbg(&dev->pdev->dev, "aliveness settled after %d msecs\n", + jiffies_to_msecs(timeout - err)); + hw->recvd_aliveness = false; + return ret; +} + +/** + * mei_txe_aliveness_set_sync - sets an wait for aliveness to complete + * + * @dev: the device structure + * + * returns returns 0 on success and < 0 otherwise + */ +int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req) +{ + if (mei_txe_aliveness_set(dev, req)) + return mei_txe_aliveness_wait(dev, req); + return 0; +} + +/** + * mei_txe_input_ready_interrupt_enable - sets the Input Ready Interrupt + * + * @dev: the device structure + */ +static void mei_txe_input_ready_interrupt_enable(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + u32 hintmsk; + /* Enable the SEC_IPC_HOST_INT_MASK_IN_RDY interrupt */ + hintmsk = mei_txe_sec_reg_read(hw, SEC_IPC_HOST_INT_MASK_REG); + hintmsk |= SEC_IPC_HOST_INT_MASK_IN_RDY; + mei_txe_sec_reg_write(hw, SEC_IPC_HOST_INT_MASK_REG, hintmsk); +} + +/** + * mei_txe_input_doorbell_set + * - Sets bit 0 in SEC_IPC_INPUT_DOORBELL.IPC_INPUT_DOORBELL. + * @dev: the device structure + */ +static void mei_txe_input_doorbell_set(struct mei_txe_hw *hw) +{ + /* Clear the interrupt cause */ + clear_bit(TXE_INTR_IN_READY_BIT, &hw->intr_cause); + mei_txe_sec_reg_write(hw, SEC_IPC_INPUT_DOORBELL_REG, 1); +} + +/** + * mei_txe_output_ready_set - Sets the SICR_SEC_IPC_OUTPUT_STATUS bit to 1 + * + * @dev: the device structure + */ +static void mei_txe_output_ready_set(struct mei_txe_hw *hw) +{ + mei_txe_br_reg_write(hw, + SICR_SEC_IPC_OUTPUT_STATUS_REG, + SEC_IPC_OUTPUT_STATUS_RDY); +} + +/** + * mei_txe_is_input_ready - check if TXE is ready for receiving data + * + * @dev: the device structure + */ +static bool mei_txe_is_input_ready(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + u32 status; + status = mei_txe_sec_reg_read(hw, SEC_IPC_INPUT_STATUS_REG); + return !!(SEC_IPC_INPUT_STATUS_RDY & status); +} + +/** + * mei_txe_intr_clear - clear all interrupts + * + * @dev: the device structure + */ +static inline void mei_txe_intr_clear(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_sec_reg_write_silent(hw, SEC_IPC_HOST_INT_STATUS_REG, + SEC_IPC_HOST_INT_STATUS_PENDING); + mei_txe_br_reg_write(hw, HISR_REG, HISR_INT_STS_MSK); + mei_txe_br_reg_write(hw, HHISR_REG, IPC_HHIER_MSK); +} + +/** + * mei_txe_intr_disable - disable all interrupts + * + * @dev: the device structure + */ +static void mei_txe_intr_disable(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, HHIER_REG, 0); + mei_txe_br_reg_write(hw, HIER_REG, 0); +} +/** + * mei_txe_intr_disable - enable all interrupts + * + * @dev: the device structure + */ +static void mei_txe_intr_enable(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, HHIER_REG, IPC_HHIER_MSK); + mei_txe_br_reg_write(hw, HIER_REG, HIER_INT_EN_MSK); +} + +/** + * mei_txe_pending_interrupts - check if there are pending interrupts + * only Aliveness, Input ready, and output doorbell are of relevance + * + * @dev: the device structure + * + * Checks if there are pending interrupts + * only Aliveness, Readiness, Input ready, and Output doorbell are relevant + */ +static bool mei_txe_pending_interrupts(struct mei_device *dev) +{ + + struct mei_txe_hw *hw = to_txe_hw(dev); + bool ret = (hw->intr_cause & (TXE_INTR_READINESS | + TXE_INTR_ALIVENESS | + TXE_INTR_IN_READY | + TXE_INTR_OUT_DB)); + + if (ret) { + dev_dbg(&dev->pdev->dev, + "Pending Interrupts InReady=%01d Readiness=%01d, Aliveness=%01d, OutDoor=%01d\n", + !!(hw->intr_cause & TXE_INTR_IN_READY), + !!(hw->intr_cause & TXE_INTR_READINESS), + !!(hw->intr_cause & TXE_INTR_ALIVENESS), + !!(hw->intr_cause & TXE_INTR_OUT_DB)); + } + return ret; +} + +/** + * mei_txe_input_payload_write - write a dword to the host buffer + * at offset idx + * + * @dev: the device structure + * @idx: index in the host buffer + * @value: value + */ +static void mei_txe_input_payload_write(struct mei_device *dev, + unsigned long idx, u32 value) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_sec_reg_write(hw, SEC_IPC_INPUT_PAYLOAD_REG + + (idx * sizeof(u32)), value); +} + +/** + * mei_txe_out_data_read - read dword from the device buffer + * at offset idx + * + * @dev: the device structure + * @idx: index in the device buffer + * + * returns register value at index + */ +static u32 mei_txe_out_data_read(const struct mei_device *dev, + unsigned long idx) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + return mei_txe_br_reg_read(hw, + BRIDGE_IPC_OUTPUT_PAYLOAD_REG + (idx * sizeof(u32))); +} + +/* Readiness */ + +/** + * mei_txe_readiness_set_host_rdy + * + * @dev: the device structure + */ +static void mei_txe_readiness_set_host_rdy(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, + SICR_HOST_IPC_READINESS_REQ_REG, + SICR_HOST_IPC_READINESS_HOST_RDY); +} + +/** + * mei_txe_readiness_clear + * + * @dev: the device structure + */ +static void mei_txe_readiness_clear(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + mei_txe_br_reg_write(hw, SICR_HOST_IPC_READINESS_REQ_REG, + SICR_HOST_IPC_READINESS_RDY_CLR); +} +/** + * mei_txe_readiness_get - Reads and returns + * the HICR_SEC_IPC_READINESS register value + * + * @dev: the device structure + */ +static u32 mei_txe_readiness_get(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + return mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG); +} + + +/** + * mei_txe_readiness_is_sec_rdy - check readiness + * for HICR_SEC_IPC_READINESS_SEC_RDY + * + * @readiness - cached readiness state + */ +static inline bool mei_txe_readiness_is_sec_rdy(u32 readiness) +{ + return !!(readiness & HICR_SEC_IPC_READINESS_SEC_RDY); +} + +/** + * mei_txe_hw_is_ready - check if the hw is ready + * + * @dev: the device structure + */ +static bool mei_txe_hw_is_ready(struct mei_device *dev) +{ + u32 readiness = mei_txe_readiness_get(dev); + return mei_txe_readiness_is_sec_rdy(readiness); +} + +/** + * mei_txe_host_is_ready - check if the host is ready + * + * @dev: the device structure + */ +static inline bool mei_txe_host_is_ready(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + u32 reg = mei_txe_br_reg_read(hw, HICR_SEC_IPC_READINESS_REG); + return !!(reg & HICR_SEC_IPC_READINESS_HOST_RDY); +} + +/** + * mei_txe_readiness_wait - wait till readiness settles + * + * @dev: the device structure + * + * returns 0 on success and -ETIME on timeout + */ +static int mei_txe_readiness_wait(struct mei_device *dev) +{ + if (mei_txe_hw_is_ready(dev)) + return 0; + + mutex_unlock(&dev->device_lock); + wait_event_timeout(dev->wait_hw_ready, dev->recvd_hw_ready, + msecs_to_jiffies(SEC_RESET_WAIT_TIMEOUT)); + mutex_lock(&dev->device_lock); + if (!dev->recvd_hw_ready) { + dev_err(&dev->pdev->dev, "wait for readiness failed\n"); + return -ETIME; + } + + dev->recvd_hw_ready = false; + return 0; +} + +/** + * mei_txe_hw_config - configure hardware at the start of the devices + * + * @dev: the device structure + * + * Configure hardware at the start of the device should be done only + * once at the device probe time + */ +static void mei_txe_hw_config(struct mei_device *dev) +{ + + struct mei_txe_hw *hw = to_txe_hw(dev); + /* Doesn't change in runtime */ + dev->hbuf_depth = PAYLOAD_SIZE / 4; + + hw->aliveness = mei_txe_aliveness_get(dev); + hw->readiness = mei_txe_readiness_get(dev); + + dev_dbg(&dev->pdev->dev, "aliveness_resp = 0x%08x, readiness = 0x%08x.\n", + hw->aliveness, hw->readiness); +} + + +/** + * mei_txe_write - writes a message to device. + * + * @dev: the device structure + * @header: header of message + * @buf: message buffer will be written + * returns 1 if success, 0 - otherwise. + */ + +static int mei_txe_write(struct mei_device *dev, + struct mei_msg_hdr *header, unsigned char *buf) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + unsigned long rem; + unsigned long length; + int slots = dev->hbuf_depth; + u32 *reg_buf = (u32 *)buf; + u32 dw_cnt; + int i; + + if (WARN_ON(!header || !buf)) + return -EINVAL; + + length = header->length; + + dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(header)); + + dw_cnt = mei_data2slots(length); + if (dw_cnt > slots) + return -EMSGSIZE; + + if (WARN(!hw->aliveness, "txe write: aliveness not asserted\n")) + return -EAGAIN; + + /* Enable Input Ready Interrupt. */ + mei_txe_input_ready_interrupt_enable(dev); + + if (!mei_txe_is_input_ready(dev)) { + dev_err(&dev->pdev->dev, "Input is not ready"); + return -EAGAIN; + } + + mei_txe_input_payload_write(dev, 0, *((u32 *)header)); + + for (i = 0; i < length / 4; i++) + mei_txe_input_payload_write(dev, i + 1, reg_buf[i]); + + rem = length & 0x3; + if (rem > 0) { + u32 reg = 0; + memcpy(®, &buf[length - rem], rem); + mei_txe_input_payload_write(dev, i + 1, reg); + } + + /* after each write the whole buffer is consumed */ + hw->slots = 0; + + /* Set Input-Doorbell */ + mei_txe_input_doorbell_set(hw); + + return 0; +} + +/** + * mei_txe_hbuf_max_len - mimics the me hbuf circular buffer + * + * @dev: the device structure + * + * returns the PAYLOAD_SIZE - 4 + */ +static size_t mei_txe_hbuf_max_len(const struct mei_device *dev) +{ + return PAYLOAD_SIZE - sizeof(struct mei_msg_hdr); +} + +/** + * mei_txe_hbuf_empty_slots - mimics the me hbuf circular buffer + * + * @dev: the device structure + * + * returns always hbuf_depth + */ +static int mei_txe_hbuf_empty_slots(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + return hw->slots; +} + +/** + * mei_txe_count_full_read_slots - mimics the me device circular buffer + * + * @dev: the device structure + * + * returns always buffer size in dwords count + */ +static int mei_txe_count_full_read_slots(struct mei_device *dev) +{ + /* read buffers has static size */ + return PAYLOAD_SIZE / 4; +} + +/** + * mei_txe_read_hdr - read message header which is always in 4 first bytes + * + * @dev: the device structure + * + * returns mei message header + */ + +static u32 mei_txe_read_hdr(const struct mei_device *dev) +{ + return mei_txe_out_data_read(dev, 0); +} +/** + * mei_txe_read - reads a message from the txe device. + * + * @dev: the device structure + * @buf: message buffer will be written + * @len: message size will be read + * + * returns -EINVAL on error wrong argument and 0 on success + */ +static int mei_txe_read(struct mei_device *dev, + unsigned char *buf, unsigned long len) +{ + + struct mei_txe_hw *hw = to_txe_hw(dev); + u32 i; + u32 *reg_buf = (u32 *)buf; + u32 rem = len & 0x3; + + if (WARN_ON(!buf || !len)) + return -EINVAL; + + dev_dbg(&dev->pdev->dev, + "buffer-length = %lu buf[0]0x%08X\n", + len, mei_txe_out_data_read(dev, 0)); + + for (i = 0; i < len / 4; i++) { + /* skip header: index starts from 1 */ + u32 reg = mei_txe_out_data_read(dev, i + 1); + dev_dbg(&dev->pdev->dev, "buf[%d] = 0x%08X\n", i, reg); + *reg_buf++ = reg; + } + + if (rem) { + u32 reg = mei_txe_out_data_read(dev, i + 1); + memcpy(reg_buf, ®, rem); + } + + mei_txe_output_ready_set(hw); + return 0; +} + +/** + * mei_txe_hw_reset - resets host and fw. + * + * @dev: the device structure + * @intr_enable: if interrupt should be enabled after reset. + * + * returns 0 on success and < 0 in case of error + */ +static int mei_txe_hw_reset(struct mei_device *dev, bool intr_enable) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + + u32 aliveness_req; + /* + * read input doorbell to ensure consistency between Bridge and SeC + * return value might be garbage return + */ + (void)mei_txe_sec_reg_read_silent(hw, SEC_IPC_INPUT_DOORBELL_REG); + + aliveness_req = mei_txe_aliveness_req_get(dev); + hw->aliveness = mei_txe_aliveness_get(dev); + + /* Disable interrupts in this stage we will poll */ + mei_txe_intr_disable(dev); + + /* + * If Aliveness Request and Aliveness Response are not equal then + * wait for them to be equal + * Since we might have interrupts disabled - poll for it + */ + if (aliveness_req != hw->aliveness) + if (mei_txe_aliveness_poll(dev, aliveness_req) < 0) { + dev_err(&dev->pdev->dev, + "wait for aliveness settle failed ... bailing out\n"); + return -EIO; + } + + /* + * If Aliveness Request and Aliveness Response are set then clear them + */ + if (aliveness_req) { + mei_txe_aliveness_set(dev, 0); + if (mei_txe_aliveness_poll(dev, 0) < 0) { + dev_err(&dev->pdev->dev, + "wait for aliveness failed ... bailing out\n"); + return -EIO; + } + } + + /* + * Set rediness RDY_CLR bit + */ + mei_txe_readiness_clear(dev); + + return 0; +} + +/** + * mei_txe_hw_start - start the hardware after reset + * + * @dev: the device structure + * + * returns 0 on success and < 0 in case of error + */ +static int mei_txe_hw_start(struct mei_device *dev) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + int ret; + + u32 hisr; + + /* bring back interrupts */ + mei_txe_intr_enable(dev); + + ret = mei_txe_readiness_wait(dev); + if (ret < 0) { + dev_err(&dev->pdev->dev, "wating for readiness failed\n"); + return ret; + } + + /* + * If HISR.INT2_STS interrupt status bit is set then clear it. + */ + hisr = mei_txe_br_reg_read(hw, HISR_REG); + if (hisr & HISR_INT_2_STS) + mei_txe_br_reg_write(hw, HISR_REG, HISR_INT_2_STS); + + /* Clear the interrupt cause of OutputDoorbell */ + clear_bit(TXE_INTR_OUT_DB_BIT, &hw->intr_cause); + + ret = mei_txe_aliveness_set_sync(dev, 1); + if (ret < 0) { + dev_err(&dev->pdev->dev, "wait for aliveness failed ... bailing out\n"); + return ret; + } + + /* enable input ready interrupts: + * SEC_IPC_HOST_INT_MASK.IPC_INPUT_READY_INT_MASK + */ + mei_txe_input_ready_interrupt_enable(dev); + + + /* Set the SICR_SEC_IPC_OUTPUT_STATUS.IPC_OUTPUT_READY bit */ + mei_txe_output_ready_set(hw); + + /* Set bit SICR_HOST_IPC_READINESS.HOST_RDY + */ + mei_txe_readiness_set_host_rdy(dev); + + return 0; +} + +/** + * mei_txe_check_and_ack_intrs - translate multi BAR interrupt into + * single bit mask and acknowledge the interrupts + * + * @dev: the device structure + * @do_ack: acknowledge interrupts + */ +static bool mei_txe_check_and_ack_intrs(struct mei_device *dev, bool do_ack) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + u32 hisr; + u32 hhisr; + u32 ipc_isr; + u32 aliveness; + bool generated; + + /* read interrupt registers */ + hhisr = mei_txe_br_reg_read(hw, HHISR_REG); + generated = (hhisr & IPC_HHIER_MSK); + if (!generated) + goto out; + + hisr = mei_txe_br_reg_read(hw, HISR_REG); + + aliveness = mei_txe_aliveness_get(dev); + if (hhisr & IPC_HHIER_SEC && aliveness) + ipc_isr = mei_txe_sec_reg_read_silent(hw, + SEC_IPC_HOST_INT_STATUS_REG); + else + ipc_isr = 0; + + generated = generated || + (hisr & HISR_INT_STS_MSK) || + (ipc_isr & SEC_IPC_HOST_INT_STATUS_PENDING); + + if (generated && do_ack) { + /* Save the interrupt causes */ + hw->intr_cause |= hisr & HISR_INT_STS_MSK; + if (ipc_isr & SEC_IPC_HOST_INT_STATUS_IN_RDY) + hw->intr_cause |= TXE_INTR_IN_READY; + + + mei_txe_intr_disable(dev); + /* Clear the interrupts in hierarchy: + * IPC and Bridge, than the High Level */ + mei_txe_sec_reg_write_silent(hw, + SEC_IPC_HOST_INT_STATUS_REG, ipc_isr); + mei_txe_br_reg_write(hw, HISR_REG, hisr); + mei_txe_br_reg_write(hw, HHISR_REG, hhisr); + } + +out: + return generated; +} + +/** + * mei_txe_irq_quick_handler - The ISR of the MEI device + * + * @irq: The irq number + * @dev_id: pointer to the device structure + * + * returns irqreturn_t + */ +irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id) +{ + struct mei_device *dev = dev_id; + + if (mei_txe_check_and_ack_intrs(dev, true)) + return IRQ_WAKE_THREAD; + return IRQ_NONE; +} + + +/** + * mei_txe_irq_thread_handler - txe interrupt thread + * + * @irq: The irq number + * @dev_id: pointer to the device structure + * + * returns irqreturn_t + * + */ +irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id) +{ + struct mei_device *dev = (struct mei_device *) dev_id; + struct mei_txe_hw *hw = to_txe_hw(dev); + struct mei_cl_cb complete_list; + s32 slots; + int rets = 0; + + dev_dbg(&dev->pdev->dev, "irq thread: Interrupt Registers HHISR|HISR|SEC=%02X|%04X|%02X\n", + mei_txe_br_reg_read(hw, HHISR_REG), + mei_txe_br_reg_read(hw, HISR_REG), + mei_txe_sec_reg_read_silent(hw, SEC_IPC_HOST_INT_STATUS_REG)); + + + /* initialize our complete list */ + mutex_lock(&dev->device_lock); + mei_io_list_init(&complete_list); + + if (pci_dev_msi_enabled(dev->pdev)) + mei_txe_check_and_ack_intrs(dev, true); + + /* show irq events */ + mei_txe_pending_interrupts(dev); + + hw->aliveness = mei_txe_aliveness_get(dev); + hw->readiness = mei_txe_readiness_get(dev); + + /* Readiness: + * Detection of TXE driver going through reset + * or TXE driver resetting the HECI interface. + */ + if (test_and_clear_bit(TXE_INTR_READINESS_BIT, &hw->intr_cause)) { + dev_dbg(&dev->pdev->dev, "Readiness Interrupt was received...\n"); + + /* Check if SeC is going through reset */ + if (mei_txe_readiness_is_sec_rdy(hw->readiness)) { + dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); + dev->recvd_hw_ready = true; + } else { + dev->recvd_hw_ready = false; + if (dev->dev_state != MEI_DEV_RESETTING) { + + dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n"); + schedule_work(&dev->reset_work); + goto end; + + } + } + wake_up(&dev->wait_hw_ready); + } + + /************************************************************/ + /* Check interrupt cause: + * Aliveness: Detection of SeC acknowledge of host request that + * it remain alive or host cancellation of that request. + */ + + if (test_and_clear_bit(TXE_INTR_ALIVENESS_BIT, &hw->intr_cause)) { + /* Clear the interrupt cause */ + dev_dbg(&dev->pdev->dev, + "Aliveness Interrupt: Status: %d\n", hw->aliveness); + hw->recvd_aliveness = true; + if (waitqueue_active(&hw->wait_aliveness)) + wake_up(&hw->wait_aliveness); + } + + + /* Output Doorbell: + * Detection of SeC having sent output to host + */ + slots = mei_count_full_read_slots(dev); + if (test_and_clear_bit(TXE_INTR_OUT_DB_BIT, &hw->intr_cause)) { + /* Read from TXE */ + rets = mei_irq_read_handler(dev, &complete_list, &slots); + if (rets && dev->dev_state != MEI_DEV_RESETTING) { + dev_err(&dev->pdev->dev, + "mei_irq_read_handler ret = %d.\n", rets); + + schedule_work(&dev->reset_work); + goto end; + } + } + /* Input Ready: Detection if host can write to SeC */ + if (test_and_clear_bit(TXE_INTR_IN_READY_BIT, &hw->intr_cause)) { + dev->hbuf_is_ready = true; + hw->slots = dev->hbuf_depth; + } + + if (hw->aliveness && dev->hbuf_is_ready) { + /* get the real register value */ + dev->hbuf_is_ready = mei_hbuf_is_ready(dev); + rets = mei_irq_write_handler(dev, &complete_list); + if (rets && rets != -EMSGSIZE) + dev_err(&dev->pdev->dev, "mei_irq_write_handler ret = %d.\n", + rets); + dev->hbuf_is_ready = mei_hbuf_is_ready(dev); + } + + mei_irq_compl_handler(dev, &complete_list); + +end: + dev_dbg(&dev->pdev->dev, "interrupt thread end ret = %d\n", rets); + + mutex_unlock(&dev->device_lock); + + mei_enable_interrupts(dev); + return IRQ_HANDLED; +} + +static const struct mei_hw_ops mei_txe_hw_ops = { + + .host_is_ready = mei_txe_host_is_ready, + + .hw_is_ready = mei_txe_hw_is_ready, + .hw_reset = mei_txe_hw_reset, + .hw_config = mei_txe_hw_config, + .hw_start = mei_txe_hw_start, + + .intr_clear = mei_txe_intr_clear, + .intr_enable = mei_txe_intr_enable, + .intr_disable = mei_txe_intr_disable, + + .hbuf_free_slots = mei_txe_hbuf_empty_slots, + .hbuf_is_ready = mei_txe_is_input_ready, + .hbuf_max_len = mei_txe_hbuf_max_len, + + .write = mei_txe_write, + + .rdbuf_full_slots = mei_txe_count_full_read_slots, + .read_hdr = mei_txe_read_hdr, + + .read = mei_txe_read, + +}; + +/** + * mei_txe_dev_init - allocates and initializes txe hardware specific structure + * + * @pdev - pci device + * returns struct mei_device * on success or NULL; + * + */ +struct mei_device *mei_txe_dev_init(struct pci_dev *pdev) +{ + struct mei_device *dev; + struct mei_txe_hw *hw; + + dev = kzalloc(sizeof(struct mei_device) + + sizeof(struct mei_txe_hw), GFP_KERNEL); + if (!dev) + return NULL; + + mei_device_init(dev); + + hw = to_txe_hw(dev); + + init_waitqueue_head(&hw->wait_aliveness); + + dev->ops = &mei_txe_hw_ops; + + dev->pdev = pdev; + return dev; +} + +/** + * mei_txe_setup_satt2 - SATT2 configuration for DMA support. + * + * @dev: the device structure + * @addr: physical address start of the range + * @range: physical range size + */ +int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range) +{ + struct mei_txe_hw *hw = to_txe_hw(dev); + + u32 lo32 = lower_32_bits(addr); + u32 hi32 = upper_32_bits(addr); + u32 ctrl; + + /* SATT is limited to 36 Bits */ + if (hi32 & ~0xF) + return -EINVAL; + + /* SATT has to be 16Byte aligned */ + if (lo32 & 0xF) + return -EINVAL; + + /* SATT range has to be 4Bytes aligned */ + if (range & 0x4) + return -EINVAL; + + /* SATT is limited to 32 MB range*/ + if (range > SATT_RANGE_MAX) + return -EINVAL; + + ctrl = SATT2_CTRL_VALID_MSK; + ctrl |= hi32 << SATT2_CTRL_BR_BASE_ADDR_REG_SHIFT; + + mei_txe_br_reg_write(hw, SATT2_SAP_SIZE_REG, range); + mei_txe_br_reg_write(hw, SATT2_BRG_BA_LSB_REG, lo32); + mei_txe_br_reg_write(hw, SATT2_CTRL_REG, ctrl); + dev_dbg(&dev->pdev->dev, "SATT2: SAP_SIZE_OFFSET=0x%08X, BRG_BA_LSB_OFFSET=0x%08X, CTRL_OFFSET=0x%08X\n", + range, lo32, ctrl); + + return 0; +} diff --git a/drivers/misc/mei/hw-txe.h b/drivers/misc/mei/hw-txe.h new file mode 100644 index 000000000000..0812d98633a4 --- /dev/null +++ b/drivers/misc/mei/hw-txe.h @@ -0,0 +1,74 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2013-2014, Intel Corporation. + * + * 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. + * + */ + +#ifndef _MEI_HW_TXE_H_ +#define _MEI_HW_TXE_H_ + +#include <linux/irqreturn.h> + +#include "hw.h" +#include "hw-txe-regs.h" + +/* Flatten Hierarchy interrupt cause */ +#define TXE_INTR_READINESS_BIT 0 /* HISR_INT_0_STS */ +#define TXE_INTR_READINESS HISR_INT_0_STS +#define TXE_INTR_ALIVENESS_BIT 1 /* HISR_INT_1_STS */ +#define TXE_INTR_ALIVENESS HISR_INT_1_STS +#define TXE_INTR_OUT_DB_BIT 2 /* HISR_INT_2_STS */ +#define TXE_INTR_OUT_DB HISR_INT_2_STS +#define TXE_INTR_IN_READY_BIT 8 /* beyond HISR */ +#define TXE_INTR_IN_READY BIT(8) + +/** + * struct mei_txe_hw - txe hardware specifics + * + * @mem_addr: SeC and BRIDGE bars + * @aliveness: aliveness (power gating) state of the hardware + * @readiness: readiness state of the hardware + * @wait_aliveness: aliveness wait queue + * @recvd_aliveness: aliveness interrupt was recived + * @intr_cause: translated interrupt cause + */ +struct mei_txe_hw { + void __iomem *mem_addr[NUM_OF_MEM_BARS]; + u32 aliveness; + u32 readiness; + u32 slots; + + wait_queue_head_t wait_aliveness; + bool recvd_aliveness; + + unsigned long intr_cause; +}; + +#define to_txe_hw(dev) (struct mei_txe_hw *)((dev)->hw) + +static inline struct mei_device *hw_txe_to_mei(struct mei_txe_hw *hw) +{ + return container_of((void *)hw, struct mei_device, hw); +} + +struct mei_device *mei_txe_dev_init(struct pci_dev *pdev); + +irqreturn_t mei_txe_irq_quick_handler(int irq, void *dev_id); +irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id); + +int mei_txe_aliveness_set_sync(struct mei_device *dev, u32 req); + +int mei_txe_setup_satt2(struct mei_device *dev, phys_addr_t addr, u32 range); + + +#endif /* _MEI_HW_TXE_H_ */ diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index dd44e33ad2b6..6b476ab49b2e 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -22,7 +22,7 @@ /* * Timeouts in Seconds */ -#define MEI_INTEROP_TIMEOUT 7 /* Timeout on ready message */ +#define MEI_HW_READY_TIMEOUT 2 /* Timeout on ready message */ #define MEI_CONNECT_TIMEOUT 3 /* HPS: at least 2 seconds */ #define MEI_CL_CONNECT_TIMEOUT 15 /* HPS: Client Connect Timeout */ @@ -31,13 +31,13 @@ #define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */ #define MEI_IAMTHIF_READ_TIMER 10 /* HPS */ +#define MEI_HBM_TIMEOUT 1 /* 1 second */ /* * MEI Version */ #define HBM_MINOR_VERSION 0 #define HBM_MAJOR_VERSION 1 -#define HBM_TIMEOUT 1 /* 1 second */ /* Host bus message command opcode */ #define MEI_HBM_CMD_OP_MSK 0x7f @@ -89,19 +89,19 @@ enum mei_stop_reason_types { * Client Connect Status * used by hbm_client_connect_response.status */ -enum client_connect_status_types { - CCS_SUCCESS = 0x00, - CCS_NOT_FOUND = 0x01, - CCS_ALREADY_STARTED = 0x02, - CCS_OUT_OF_RESOURCES = 0x03, - CCS_MESSAGE_SMALL = 0x04 +enum mei_cl_connect_status { + MEI_CL_CONN_SUCCESS = 0x00, + MEI_CL_CONN_NOT_FOUND = 0x01, + MEI_CL_CONN_ALREADY_STARTED = 0x02, + MEI_CL_CONN_OUT_OF_RESOURCES = 0x03, + MEI_CL_CONN_MESSAGE_SMALL = 0x04 }; /* * Client Disconnect Status */ -enum client_disconnect_status_types { - CDS_SUCCESS = 0x00 +enum mei_cl_disconnect_status { + MEI_CL_DISCONN_SUCCESS = 0x00 }; /* diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index cdd31c2a2a2b..4460975c0eef 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -116,7 +116,6 @@ int mei_reset(struct mei_device *dev) mei_cl_unlink(&dev->wd_cl); mei_cl_unlink(&dev->iamthif_cl); mei_amthif_reset_params(dev); - memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); } @@ -126,7 +125,6 @@ int mei_reset(struct mei_device *dev) if (ret) { dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret); - dev->dev_state = MEI_DEV_DISABLED; return ret; } @@ -139,7 +137,6 @@ int mei_reset(struct mei_device *dev) ret = mei_hw_start(dev); if (ret) { dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret); - dev->dev_state = MEI_DEV_DISABLED; return ret; } @@ -149,7 +146,7 @@ int mei_reset(struct mei_device *dev) ret = mei_hbm_start_req(dev); if (ret) { dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret); - dev->dev_state = MEI_DEV_DISABLED; + dev->dev_state = MEI_DEV_RESETTING; return ret; } @@ -166,6 +163,7 @@ EXPORT_SYMBOL_GPL(mei_reset); */ int mei_start(struct mei_device *dev) { + int ret; mutex_lock(&dev->device_lock); /* acknowledge interrupt and stop interrupts */ @@ -175,10 +173,18 @@ int mei_start(struct mei_device *dev) dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); - dev->dev_state = MEI_DEV_INITIALIZING; dev->reset_count = 0; - mei_reset(dev); + do { + dev->dev_state = MEI_DEV_INITIALIZING; + ret = mei_reset(dev); + + if (ret == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) { + dev_err(&dev->pdev->dev, "reset failed ret = %d", ret); + goto err; + } + } while (ret); + /* we cannot start the device w/o hbm start message completed */ if (dev->dev_state == MEI_DEV_DISABLED) { dev_err(&dev->pdev->dev, "reset failed"); goto err; @@ -238,27 +244,40 @@ int mei_restart(struct mei_device *dev) mutex_unlock(&dev->device_lock); - if (err || dev->dev_state == MEI_DEV_DISABLED) + if (err == -ENODEV || dev->dev_state == MEI_DEV_DISABLED) { + dev_err(&dev->pdev->dev, "device disabled = %d\n", err); return -ENODEV; + } + + /* try to start again */ + if (err) + schedule_work(&dev->reset_work); + return 0; } EXPORT_SYMBOL_GPL(mei_restart); - static void mei_reset_work(struct work_struct *work) { struct mei_device *dev = container_of(work, struct mei_device, reset_work); + int ret; mutex_lock(&dev->device_lock); - mei_reset(dev); + ret = mei_reset(dev); mutex_unlock(&dev->device_lock); - if (dev->dev_state == MEI_DEV_DISABLED) - dev_err(&dev->pdev->dev, "reset failed"); + if (dev->dev_state == MEI_DEV_DISABLED) { + dev_err(&dev->pdev->dev, "device disabled = %d\n", ret); + return; + } + + /* retry reset in case of failure */ + if (ret) + schedule_work(&dev->reset_work); } void mei_stop(struct mei_device *dev) @@ -269,6 +288,8 @@ void mei_stop(struct mei_device *dev) mei_nfc_host_exit(dev); + mei_cl_bus_remove_devices(dev); + mutex_lock(&dev->device_lock); mei_wd_stop(dev); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index f0fbb5179f80..29b5af8efb71 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -26,7 +26,6 @@ #include "mei_dev.h" #include "hbm.h" -#include "hw-me.h" #include "client.h" @@ -161,29 +160,63 @@ static int mei_cl_irq_read_msg(struct mei_device *dev, } /** + * mei_cl_irq_disconnect_rsp - send disconnection response message + * + * @cl: client + * @cb: callback block. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, + struct mei_cl_cb *cmpl_list) +{ + struct mei_device *dev = cl->dev; + u32 msg_slots; + int slots; + int ret; + + slots = mei_hbuf_empty_slots(dev); + msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_response)); + + if (slots < msg_slots) + return -EMSGSIZE; + + ret = mei_hbm_cl_disconnect_rsp(dev, cl); + + cl->state = MEI_FILE_DISCONNECTED; + cl->status = 0; + list_del(&cb->list); + mei_io_cb_free(cb); + + return ret; +} + + + +/** * mei_cl_irq_close - processes close related operation from * interrupt thread context - send disconnect request * * @cl: client * @cb: callback block. - * @slots: free slots. * @cmpl_list: complete list. * * returns 0, OK; otherwise, error. */ static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb, - s32 *slots, struct mei_cl_cb *cmpl_list) + struct mei_cl_cb *cmpl_list) { struct mei_device *dev = cl->dev; + u32 msg_slots; + int slots; - u32 msg_slots = - mei_data2slots(sizeof(struct hbm_client_connect_request)); + msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); + slots = mei_hbuf_empty_slots(dev); - if (*slots < msg_slots) + if (slots < msg_slots) return -EMSGSIZE; - *slots -= msg_slots; - if (mei_hbm_cl_disconnect_req(dev, cl)) { cl->status = 0; cb->buf_idx = 0; @@ -207,27 +240,23 @@ static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb, * * @cl: client * @cb: callback block. - * @slots: free slots. * @cmpl_list: complete list. * * returns 0, OK; otherwise, error. */ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, - s32 *slots, struct mei_cl_cb *cmpl_list) + struct mei_cl_cb *cmpl_list) { struct mei_device *dev = cl->dev; - u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); - + u32 msg_slots; + int slots; int ret; + msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); + slots = mei_hbuf_empty_slots(dev); - if (*slots < msg_slots) { - /* return the cancel routine */ - list_del(&cb->list); + if (slots < msg_slots) return -EMSGSIZE; - } - - *slots -= msg_slots; ret = mei_hbm_cl_flow_control_req(dev, cl); if (ret) { @@ -244,32 +273,30 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, /** - * mei_cl_irq_ioctl - processes client ioctl related operation from the - * interrupt thread context - send connection request + * mei_cl_irq_connect - send connect request in irq_thread context * * @cl: client * @cb: callback block. - * @slots: free slots. * @cmpl_list: complete list. * * returns 0, OK; otherwise, error. */ -static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb, - s32 *slots, struct mei_cl_cb *cmpl_list) +static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, + struct mei_cl_cb *cmpl_list) { struct mei_device *dev = cl->dev; + u32 msg_slots; + int slots; int ret; - u32 msg_slots = - mei_data2slots(sizeof(struct hbm_client_connect_request)); + msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); + slots = mei_hbuf_empty_slots(dev); - if (*slots < msg_slots) { - /* return the cancel routine */ - list_del(&cb->list); - return -EMSGSIZE; - } + if (mei_cl_is_other_connecting(cl)) + return 0; - *slots -= msg_slots; + if (slots < msg_slots) + return -EMSGSIZE; cl->state = MEI_FILE_CONNECTING; @@ -323,7 +350,7 @@ int mei_irq_read_handler(struct mei_device *dev, dev_err(&dev->pdev->dev, "less data available than length=%08x.\n", *slots); /* we can't read the message */ - ret = -ERANGE; + ret = -ENODATA; goto end; } @@ -409,10 +436,10 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) s32 slots; int ret; - if (!mei_hbuf_is_ready(dev)) { - dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n"); + + if (!mei_hbuf_acquire(dev)) return 0; - } + slots = mei_hbuf_empty_slots(dev); if (slots <= 0) return -EMSGSIZE; @@ -447,29 +474,16 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) if (dev->wd_state == MEI_WD_STOPPING) { dev->wd_state = MEI_WD_IDLE; - wake_up_interruptible(&dev->wait_stop_wd); + wake_up(&dev->wait_stop_wd); } - if (dev->wr_ext_msg.hdr.length) { - mei_write_message(dev, &dev->wr_ext_msg.hdr, - dev->wr_ext_msg.data); - slots -= mei_data2slots(dev->wr_ext_msg.hdr.length); - dev->wr_ext_msg.hdr.length = 0; - } - if (dev->dev_state == MEI_DEV_ENABLED) { + if (mei_cl_is_connected(&dev->wd_cl)) { if (dev->wd_pending && mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) { - if (mei_wd_send(dev)) - dev_dbg(&dev->pdev->dev, "wd send failed.\n"); - else if (mei_cl_flow_ctrl_reduce(&dev->wd_cl)) - return -ENODEV; - + ret = mei_wd_send(dev); + if (ret) + return ret; dev->wd_pending = false; - - if (dev->wd_state == MEI_WD_RUNNING) - slots -= mei_data2slots(MEI_WD_START_MSG_SIZE); - else - slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE); } } @@ -484,28 +498,31 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) switch (cb->fop_type) { case MEI_FOP_CLOSE: /* send disconnect message */ - ret = mei_cl_irq_close(cl, cb, &slots, cmpl_list); + ret = mei_cl_irq_close(cl, cb, cmpl_list); if (ret) return ret; break; case MEI_FOP_READ: /* send flow control message */ - ret = mei_cl_irq_read(cl, cb, &slots, cmpl_list); + ret = mei_cl_irq_read(cl, cb, cmpl_list); if (ret) return ret; break; - case MEI_FOP_IOCTL: + case MEI_FOP_CONNECT: /* connect message */ - if (mei_cl_is_other_connecting(cl)) - continue; - ret = mei_cl_irq_ioctl(cl, cb, &slots, cmpl_list); + ret = mei_cl_irq_connect(cl, cb, cmpl_list); if (ret) return ret; break; - + case MEI_FOP_DISCONNECT_RSP: + /* send disconnect resp */ + ret = mei_cl_irq_disconnect_rsp(cl, cb, cmpl_list); + if (ret) + return ret; + break; default: BUG(); } @@ -518,11 +535,9 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) if (cl == NULL) continue; if (cl == &dev->iamthif_cl) - ret = mei_amthif_irq_write_complete(cl, cb, - &slots, cmpl_list); + ret = mei_amthif_irq_write(cl, cb, cmpl_list); else - ret = mei_cl_irq_write_complete(cl, cb, - &slots, cmpl_list); + ret = mei_cl_irq_write(cl, cb, cmpl_list); if (ret) return ret; } @@ -541,8 +556,7 @@ EXPORT_SYMBOL_GPL(mei_irq_write_handler); void mei_timer(struct work_struct *work) { unsigned long timeout; - struct mei_cl *cl_pos = NULL; - struct mei_cl *cl_next = NULL; + struct mei_cl *cl; struct mei_cl_cb *cb_pos = NULL; struct mei_cl_cb *cb_next = NULL; @@ -570,9 +584,9 @@ void mei_timer(struct work_struct *work) goto out; /*** connect/disconnect timeouts ***/ - list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { - if (cl_pos->timer_count) { - if (--cl_pos->timer_count == 0) { + list_for_each_entry(cl, &dev->file_list, link) { + if (cl->timer_count) { + if (--cl->timer_count == 0) { dev_err(&dev->pdev->dev, "timer: connect/disconnect timeout.\n"); mei_reset(dev); goto out; @@ -580,6 +594,9 @@ void mei_timer(struct work_struct *work) } } + if (!mei_cl_is_connected(&dev->iamthif_cl)) + goto out; + if (dev->iamthif_stall_timer) { if (--dev->iamthif_stall_timer == 0) { dev_err(&dev->pdev->dev, "timer: amthif hanged.\n"); @@ -619,10 +636,10 @@ void mei_timer(struct work_struct *work) list_for_each_entry_safe(cb_pos, cb_next, &dev->amthif_rd_complete_list.list, list) { - cl_pos = cb_pos->file_object->private_data; + cl = cb_pos->file_object->private_data; /* Finding the AMTHI entry. */ - if (cl_pos == &dev->iamthif_cl) + if (cl == &dev->iamthif_cl) list_del(&cb_pos->list); } mei_io_cb_free(dev->iamthif_current_cb); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 5424f8ff3f7f..b35594dbf52f 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -13,9 +13,6 @@ * more details. * */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -40,7 +37,6 @@ #include <linux/mei.h> #include "mei_dev.h" -#include "hw-me.h" #include "client.h" /** @@ -129,17 +125,11 @@ static int mei_release(struct inode *inode, struct file *file) } if (cl->state == MEI_FILE_CONNECTED) { cl->state = MEI_FILE_DISCONNECTING; - dev_dbg(&dev->pdev->dev, - "disconnecting client host client = %d, " - "ME client = %d\n", - cl->host_client_id, - cl->me_client_id); + cl_dbg(dev, cl, "disconnecting\n"); rets = mei_cl_disconnect(cl); } mei_cl_flush_queues(cl); - dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n", - cl->host_client_id, - cl->me_client_id); + cl_dbg(dev, cl, "removing\n"); mei_cl_unlink(cl); @@ -284,6 +274,7 @@ copy_buffer: length = min_t(size_t, length, cb->buf_idx - *offset); if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { + dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n"); rets = -EFAULT; goto free; } @@ -340,7 +331,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, id = mei_me_cl_by_id(dev, cl->me_client_id); if (id < 0) { - rets = -ENODEV; + rets = -ENOTTY; goto out; } @@ -404,7 +395,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf, rets = copy_from_user(write_cb->request_buffer.data, ubuf, length); if (rets) { - dev_err(&dev->pdev->dev, "failed to copy data from userland\n"); + dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } @@ -471,7 +462,7 @@ static int mei_ioctl_connect_client(struct file *file, if (i < 0 || dev->me_clients[i].props.fixed_address) { dev_dbg(&dev->pdev->dev, "Cannot connect to FW Client UUID = %pUl\n", &data->in_client_uuid); - rets = -ENODEV; + rets = -ENOTTY; goto end; } @@ -569,7 +560,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) dev_dbg(&dev->pdev->dev, "copy connect data from user\n"); if (copy_from_user(connect_data, (char __user *)data, sizeof(struct mei_connect_client_data))) { - dev_err(&dev->pdev->dev, "failed to copy data from userland\n"); + dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n"); rets = -EFAULT; goto out; } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index f7de95b4cdd9..94a516716d22 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -24,7 +24,6 @@ #include <linux/mei_cl_bus.h> #include "hw.h" -#include "hw-me-regs.h" #include "hbm.h" /* @@ -130,16 +129,18 @@ enum mei_wd_states { /** * enum mei_cb_file_ops - file operation associated with the callback - * @MEI_FOP_READ - read - * @MEI_FOP_WRITE - write - * @MEI_FOP_IOCTL - ioctl - * @MEI_FOP_OPEN - open - * @MEI_FOP_CLOSE - close + * @MEI_FOP_READ - read + * @MEI_FOP_WRITE - write + * @MEI_FOP_CONNECT - connect + * @MEI_FOP_DISCONNECT_RSP - disconnect response + * @MEI_FOP_OPEN - open + * @MEI_FOP_CLOSE - close */ enum mei_cb_file_ops { MEI_FOP_READ = 0, MEI_FOP_WRITE, - MEI_FOP_IOCTL, + MEI_FOP_CONNECT, + MEI_FOP_DISCONNECT_RSP, MEI_FOP_OPEN, MEI_FOP_CLOSE }; @@ -236,20 +237,20 @@ struct mei_cl { */ struct mei_hw_ops { - bool (*host_is_ready) (struct mei_device *dev); + bool (*host_is_ready)(struct mei_device *dev); - bool (*hw_is_ready) (struct mei_device *dev); - int (*hw_reset) (struct mei_device *dev, bool enable); - int (*hw_start) (struct mei_device *dev); - void (*hw_config) (struct mei_device *dev); + bool (*hw_is_ready)(struct mei_device *dev); + int (*hw_reset)(struct mei_device *dev, bool enable); + int (*hw_start)(struct mei_device *dev); + void (*hw_config)(struct mei_device *dev); - void (*intr_clear) (struct mei_device *dev); - void (*intr_enable) (struct mei_device *dev); - void (*intr_disable) (struct mei_device *dev); + void (*intr_clear)(struct mei_device *dev); + void (*intr_enable)(struct mei_device *dev); + void (*intr_disable)(struct mei_device *dev); - int (*hbuf_free_slots) (struct mei_device *dev); - bool (*hbuf_is_ready) (struct mei_device *dev); - size_t (*hbuf_max_len) (const struct mei_device *dev); + int (*hbuf_free_slots)(struct mei_device *dev); + bool (*hbuf_is_ready)(struct mei_device *dev); + size_t (*hbuf_max_len)(const struct mei_device *dev); int (*write)(struct mei_device *dev, struct mei_msg_hdr *hdr, @@ -258,7 +259,7 @@ struct mei_hw_ops { int (*rdbuf_full_slots)(struct mei_device *dev); u32 (*read_hdr)(const struct mei_device *dev); - int (*read) (struct mei_device *dev, + int (*read)(struct mei_device *dev, unsigned char *buf, unsigned long len); }; @@ -294,6 +295,7 @@ int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length); int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length); int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); void mei_cl_bus_rx_event(struct mei_cl *cl); +void mei_cl_bus_remove_devices(struct mei_device *dev); int mei_cl_bus_init(void); void mei_cl_bus_exit(void); @@ -339,7 +341,6 @@ struct mei_cl_device { * @hbuf_depth - depth of hardware host/write buffer is slots * @hbuf_is_ready - query if the host host/write buffer is ready * @wr_msg - the buffer for hbm control messages - * @wr_ext_msg - the buffer for hbm control responses (set in read cycle) */ struct mei_device { struct pci_dev *pdev; /* pointer to pci device struct */ @@ -394,11 +395,6 @@ struct mei_device { unsigned char data[128]; } wr_msg; - struct { - struct mei_msg_hdr hdr; - unsigned char data[4]; /* All HBM messages are 4 bytes */ - } wr_ext_msg; /* for control responses */ - struct hbm_version version; struct mei_me_client *me_clients; /* Note: memory has to be allocated */ @@ -518,8 +514,8 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, void mei_amthif_run_next_cmd(struct mei_device *dev); -int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, - s32 *slots, struct mei_cl_cb *cmpl_list); +int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, + struct mei_cl_cb *cmpl_list); void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb); int mei_amthif_irq_read_msg(struct mei_device *dev, @@ -546,7 +542,7 @@ int mei_wd_host_init(struct mei_device *dev); * once we got connection to the WD Client * @dev - mei device */ -void mei_watchdog_register(struct mei_device *dev); +int mei_watchdog_register(struct mei_device *dev); /* * mei_watchdog_unregister - Unregistering watchdog interface * @dev - mei device @@ -633,6 +629,8 @@ static inline int mei_count_full_read_slots(struct mei_device *dev) return dev->ops->rdbuf_full_slots(dev); } +bool mei_hbuf_acquire(struct mei_device *dev); + #if IS_ENABLED(CONFIG_DEBUG_FS) int mei_dbgfs_register(struct mei_device *dev, const char *name); void mei_dbgfs_deregister(struct mei_device *dev); diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c index a58320c0c049..3095fc514a65 100644 --- a/drivers/misc/mei/nfc.c +++ b/drivers/misc/mei/nfc.c @@ -364,7 +364,7 @@ static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length) if (!wait_event_interruptible_timeout(ndev->send_wq, ndev->recv_req_id == ndev->req_id, HZ)) { dev_err(&dev->pdev->dev, "NFC MEI command timeout\n"); - err = -ETIMEDOUT; + err = -ETIME; } else { ndev->req_id++; } @@ -502,7 +502,7 @@ int mei_nfc_host_init(struct mei_device *dev) i = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid); if (i < 0) { dev_info(&dev->pdev->dev, "nfc: failed to find the client\n"); - ret = -ENOENT; + ret = -ENOTTY; goto err; } @@ -520,7 +520,7 @@ int mei_nfc_host_init(struct mei_device *dev) i = mei_me_cl_by_uuid(dev, &mei_nfc_guid); if (i < 0) { dev_info(&dev->pdev->dev, "nfc: failed to find the client\n"); - ret = -ENOENT; + ret = -ENOTTY; goto err; } @@ -552,13 +552,7 @@ err: void mei_nfc_host_exit(struct mei_device *dev) { struct mei_nfc_dev *ndev = &nfc_dev; - cancel_work_sync(&ndev->init_work); +} - mutex_lock(&dev->device_lock); - if (ndev->cl && ndev->cl->device) - mei_cl_remove_device(ndev->cl->device); - mei_nfc_free(ndev); - mutex_unlock(&dev->device_lock); -} diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index ddadd08956f4..1c8fd3a3e135 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -13,9 +13,6 @@ * more details. * */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -27,7 +24,6 @@ #include <linux/aio.h> #include <linux/pci.h> #include <linux/poll.h> -#include <linux/init.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/sched.h> @@ -40,11 +36,12 @@ #include <linux/mei.h> #include "mei_dev.h" -#include "hw-me.h" #include "client.h" +#include "hw-me-regs.h" +#include "hw-me.h" /* mei_pci_tbl - PCI Device ID Table */ -static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = { +static const struct pci_device_id mei_me_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)}, @@ -270,7 +267,7 @@ static void mei_me_remove(struct pci_dev *pdev) } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int mei_me_pci_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); @@ -330,11 +327,12 @@ static int mei_me_pci_resume(struct device *device) return 0; } + static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume); #define MEI_ME_PM_OPS (&mei_me_pm_ops) #else #define MEI_ME_PM_OPS NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ /* * PCI driver structure */ diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c new file mode 100644 index 000000000000..ad3adb009a1e --- /dev/null +++ b/drivers/misc/mei/pci-txe.c @@ -0,0 +1,293 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2013-2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/uuid.h> +#include <linux/jiffies.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> + +#include <linux/mei.h> + + +#include "mei_dev.h" +#include "hw-txe.h" + +static const struct pci_device_id mei_txe_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F18)}, /* Baytrail */ + {0, } +}; +MODULE_DEVICE_TABLE(pci, mei_txe_pci_tbl); + + +static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw) +{ + int i; + for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) { + if (hw->mem_addr[i]) { + pci_iounmap(pdev, hw->mem_addr[i]); + hw->mem_addr[i] = NULL; + } + } +} +/** + * mei_probe - Device Initialization Routine + * + * @pdev: PCI device structure + * @ent: entry in mei_txe_pci_tbl + * + * returns 0 on success, <0 on failure. + */ +static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct mei_device *dev; + struct mei_txe_hw *hw; + int err; + int i; + + /* enable pci dev */ + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "failed to enable pci device.\n"); + goto end; + } + /* set PCI host mastering */ + pci_set_master(pdev); + /* pci request regions for mei driver */ + err = pci_request_regions(pdev, KBUILD_MODNAME); + if (err) { + dev_err(&pdev->dev, "failed to get pci regions.\n"); + goto disable_device; + } + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); + if (err) { + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&pdev->dev, "No suitable DMA available.\n"); + goto release_regions; + } + } + + /* allocates and initializes the mei dev structure */ + dev = mei_txe_dev_init(pdev); + if (!dev) { + err = -ENOMEM; + goto release_regions; + } + hw = to_txe_hw(dev); + + /* mapping IO device memory */ + for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) { + hw->mem_addr[i] = pci_iomap(pdev, i, 0); + if (!hw->mem_addr[i]) { + dev_err(&pdev->dev, "mapping I/O device memory failure.\n"); + err = -ENOMEM; + goto free_device; + } + } + + + pci_enable_msi(pdev); + + /* clear spurious interrupts */ + mei_clear_interrupts(dev); + + /* request and enable interrupt */ + if (pci_dev_msi_enabled(pdev)) + err = request_threaded_irq(pdev->irq, + NULL, + mei_txe_irq_thread_handler, + IRQF_ONESHOT, KBUILD_MODNAME, dev); + else + err = request_threaded_irq(pdev->irq, + mei_txe_irq_quick_handler, + mei_txe_irq_thread_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (err) { + dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n", + pdev->irq); + goto free_device; + } + + if (mei_start(dev)) { + dev_err(&pdev->dev, "init hw failure.\n"); + err = -ENODEV; + goto release_irq; + } + + err = mei_register(dev); + if (err) + goto release_irq; + + pci_set_drvdata(pdev, dev); + + return 0; + +release_irq: + + mei_cancel_work(dev); + + /* disable interrupts */ + mei_disable_interrupts(dev); + + free_irq(pdev->irq, dev); + pci_disable_msi(pdev); + +free_device: + mei_txe_pci_iounmap(pdev, hw); + + kfree(dev); +release_regions: + pci_release_regions(pdev); +disable_device: + pci_disable_device(pdev); +end: + dev_err(&pdev->dev, "initialization failed.\n"); + return err; +} + +/** + * mei_remove - Device Removal Routine + * + * @pdev: PCI device structure + * + * mei_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. + */ +static void mei_txe_remove(struct pci_dev *pdev) +{ + struct mei_device *dev; + struct mei_txe_hw *hw; + + dev = pci_get_drvdata(pdev); + if (!dev) { + dev_err(&pdev->dev, "mei: dev =NULL\n"); + return; + } + + hw = to_txe_hw(dev); + + mei_stop(dev); + + /* disable interrupts */ + mei_disable_interrupts(dev); + free_irq(pdev->irq, dev); + pci_disable_msi(pdev); + + pci_set_drvdata(pdev, NULL); + + mei_txe_pci_iounmap(pdev, hw); + + mei_deregister(dev); + + kfree(dev); + + pci_release_regions(pdev); + pci_disable_device(pdev); +} + + +#ifdef CONFIG_PM_SLEEP +static int mei_txe_pci_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct mei_device *dev = pci_get_drvdata(pdev); + + if (!dev) + return -ENODEV; + + dev_dbg(&pdev->dev, "suspend\n"); + + mei_stop(dev); + + mei_disable_interrupts(dev); + + free_irq(pdev->irq, dev); + pci_disable_msi(pdev); + + return 0; +} + +static int mei_txe_pci_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct mei_device *dev; + int err; + + dev = pci_get_drvdata(pdev); + if (!dev) + return -ENODEV; + + pci_enable_msi(pdev); + + mei_clear_interrupts(dev); + + /* request and enable interrupt */ + if (pci_dev_msi_enabled(pdev)) + err = request_threaded_irq(pdev->irq, + NULL, + mei_txe_irq_thread_handler, + IRQF_ONESHOT, KBUILD_MODNAME, dev); + else + err = request_threaded_irq(pdev->irq, + mei_txe_irq_quick_handler, + mei_txe_irq_thread_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (err) { + dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n", + pdev->irq); + return err; + } + + err = mei_restart(dev); + + return err; +} + +static SIMPLE_DEV_PM_OPS(mei_txe_pm_ops, + mei_txe_pci_suspend, + mei_txe_pci_resume); + +#define MEI_TXE_PM_OPS (&mei_txe_pm_ops) +#else +#define MEI_TXE_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ +/* + * PCI driver structure + */ +static struct pci_driver mei_txe_driver = { + .name = KBUILD_MODNAME, + .id_table = mei_txe_pci_tbl, + .probe = mei_txe_probe, + .remove = mei_txe_remove, + .shutdown = mei_txe_remove, + .driver.pm = MEI_TXE_PM_OPS, +}; + +module_pci_driver(mei_txe_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) Trusted Execution Environment Interface"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index f70945ed96f6..ebf1cbc198fd 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c @@ -25,7 +25,6 @@ #include "mei_dev.h" #include "hbm.h" -#include "hw-me.h" #include "client.h" static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; @@ -53,7 +52,7 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) * * @dev: the device structure * - * returns -ENENT if wd client cannot be found + * returns -ENOTTY if wd client cannot be found * -EIO if write has failed * 0 on success */ @@ -73,7 +72,7 @@ int mei_wd_host_init(struct mei_device *dev) id = mei_me_cl_by_uuid(dev, &mei_wd_guid); if (id < 0) { dev_info(&dev->pdev->dev, "wd: failed to find the client\n"); - return id; + return -ENOTTY; } cl->me_client_id = dev->me_clients[id].client_id; @@ -87,15 +86,20 @@ int mei_wd_host_init(struct mei_device *dev) cl->state = MEI_FILE_CONNECTING; - if (mei_hbm_cl_connect_req(dev, cl)) { - dev_err(&dev->pdev->dev, "wd: failed to connect to the client\n"); - cl->state = MEI_FILE_DISCONNECTED; - cl->host_client_id = 0; - return -EIO; + ret = mei_cl_connect(cl, NULL); + + if (ret) { + dev_err(&dev->pdev->dev, "wd: failed to connect = %d\n", ret); + mei_cl_unlink(cl); + return ret; } - cl->timer_count = MEI_CONNECT_TIMEOUT; - return 0; + ret = mei_watchdog_register(dev); + if (ret) { + mei_cl_disconnect(cl); + mei_cl_unlink(cl); + } + return ret; } /** @@ -106,13 +110,16 @@ int mei_wd_host_init(struct mei_device *dev) * returns 0 if success, * -EIO when message send fails * -EINVAL when invalid message is to be sent + * -ENODEV on flow control failure */ int mei_wd_send(struct mei_device *dev) { + struct mei_cl *cl = &dev->wd_cl; struct mei_msg_hdr hdr; + int ret; - hdr.host_addr = dev->wd_cl.host_client_id; - hdr.me_addr = dev->wd_cl.me_client_id; + hdr.host_addr = cl->host_client_id; + hdr.me_addr = cl->me_client_id; hdr.msg_complete = 1; hdr.reserved = 0; hdr.internal = 0; @@ -121,10 +128,24 @@ int mei_wd_send(struct mei_device *dev) hdr.length = MEI_WD_START_MSG_SIZE; else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) hdr.length = MEI_WD_STOP_MSG_SIZE; - else + else { + dev_err(&dev->pdev->dev, "wd: invalid message is to be sent, aborting\n"); return -EINVAL; + } - return mei_write_message(dev, &hdr, dev->wd_data); + ret = mei_write_message(dev, &hdr, dev->wd_data); + if (ret) { + dev_err(&dev->pdev->dev, "wd: write message failed\n"); + return ret; + } + + ret = mei_cl_flow_ctrl_reduce(cl); + if (ret) { + dev_err(&dev->pdev->dev, "wd: flow_ctrl_reduce failed.\n"); + return ret; + } + + return 0; } /** @@ -133,9 +154,11 @@ int mei_wd_send(struct mei_device *dev) * @dev: the device structure * @preserve: indicate if to keep the timeout value * - * returns 0 if success, - * -EIO when message send fails + * returns 0 if success + * on error: + * -EIO when message send fails * -EINVAL when invalid message is to be sent + * -ETIME on message timeout */ int mei_wd_stop(struct mei_device *dev) { @@ -151,20 +174,12 @@ int mei_wd_stop(struct mei_device *dev) ret = mei_cl_flow_ctrl_creds(&dev->wd_cl); if (ret < 0) - goto out; - - if (ret && dev->hbuf_is_ready) { - ret = 0; - dev->hbuf_is_ready = false; - - if (!mei_wd_send(dev)) { - ret = mei_cl_flow_ctrl_reduce(&dev->wd_cl); - if (ret) - goto out; - } else { - dev_err(&dev->pdev->dev, "wd: send stop failed\n"); - } + goto err; + if (ret && mei_hbuf_acquire(dev)) { + ret = mei_wd_send(dev); + if (ret) + goto err; dev->wd_pending = false; } else { dev->wd_pending = true; @@ -172,21 +187,21 @@ int mei_wd_stop(struct mei_device *dev) mutex_unlock(&dev->device_lock); - ret = wait_event_interruptible_timeout(dev->wait_stop_wd, - dev->wd_state == MEI_WD_IDLE, - msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); + ret = wait_event_timeout(dev->wait_stop_wd, + dev->wd_state == MEI_WD_IDLE, + msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); mutex_lock(&dev->device_lock); - if (dev->wd_state == MEI_WD_IDLE) { - dev_dbg(&dev->pdev->dev, "wd: stop completed ret=%d.\n", ret); - ret = 0; - } else { - if (!ret) - ret = -ETIMEDOUT; + if (dev->wd_state != MEI_WD_IDLE) { + /* timeout */ + ret = -ETIME; dev_warn(&dev->pdev->dev, "wd: stop failed to complete ret=%d.\n", ret); + goto err; } - -out: + dev_dbg(&dev->pdev->dev, "wd: stop completed after %u msec\n", + MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); + return 0; +err: return ret; } @@ -260,8 +275,8 @@ static int mei_wd_ops_stop(struct watchdog_device *wd_dev) */ static int mei_wd_ops_ping(struct watchdog_device *wd_dev) { - int ret = 0; struct mei_device *dev; + int ret; dev = watchdog_get_drvdata(wd_dev); if (!dev) @@ -277,25 +292,18 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev) dev->wd_state = MEI_WD_RUNNING; + ret = mei_cl_flow_ctrl_creds(&dev->wd_cl); + if (ret < 0) + goto end; /* Check if we can send the ping to HW*/ - if (dev->hbuf_is_ready && mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) { + if (ret && mei_hbuf_acquire(dev)) { - dev->hbuf_is_ready = false; dev_dbg(&dev->pdev->dev, "wd: sending ping\n"); - if (mei_wd_send(dev)) { - dev_err(&dev->pdev->dev, "wd: send failed.\n"); - ret = -EIO; + ret = mei_wd_send(dev); + if (ret) goto end; - } - - if (mei_cl_flow_ctrl_reduce(&dev->wd_cl)) { - dev_err(&dev->pdev->dev, - "wd: mei_cl_flow_ctrl_reduce() failed.\n"); - ret = -EIO; - goto end; - } - + dev->wd_pending = false; } else { dev->wd_pending = true; } @@ -363,17 +371,25 @@ static struct watchdog_device amt_wd_dev = { }; -void mei_watchdog_register(struct mei_device *dev) +int mei_watchdog_register(struct mei_device *dev) { - if (watchdog_register_device(&amt_wd_dev)) { - dev_err(&dev->pdev->dev, - "wd: unable to register watchdog device.\n"); - return; + + int ret; + + /* unlock to perserve correct locking order */ + mutex_unlock(&dev->device_lock); + ret = watchdog_register_device(&amt_wd_dev); + mutex_lock(&dev->device_lock); + if (ret) { + dev_err(&dev->pdev->dev, "wd: unable to register watchdog device = %d.\n", + ret); + return ret; } dev_dbg(&dev->pdev->dev, "wd: successfully register watchdog interface.\n"); watchdog_set_drvdata(&amt_wd_dev, dev); + return 0; } void mei_watchdog_unregister(struct mei_device *dev) diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h index 347b9b3b7916..306f502be95e 100644 --- a/drivers/misc/mic/card/mic_device.h +++ b/drivers/misc/mic/card/mic_device.h @@ -29,6 +29,7 @@ #include <linux/workqueue.h> #include <linux/io.h> +#include <linux/irqreturn.h> /** * struct mic_intr_info - Contains h/w specific interrupt sources info diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 1a6edce2ecde..0398c696d257 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -24,6 +24,7 @@ #include <linux/cdev.h> #include <linux/idr.h> #include <linux/notifier.h> +#include <linux/irqreturn.h> #include "mic_intr.h" diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c index f9c29bc918bc..dbc5afde1392 100644 --- a/drivers/misc/mic/host/mic_intr.c +++ b/drivers/misc/mic/host/mic_intr.c @@ -194,7 +194,7 @@ static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev) for (i = 0; i < MIC_MIN_MSIX; i++) mdev->irq_info.msix_entries[i].entry = i; - rc = pci_enable_msix(pdev, mdev->irq_info.msix_entries, + rc = pci_enable_msix_exact(pdev, mdev->irq_info.msix_entries, MIC_MIN_MSIX); if (rc) { dev_dbg(&pdev->dev, "Error enabling MSIx. rc = %d\n", rc); diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index a5925f7f17f6..956597321d2a 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -636,6 +636,7 @@ static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr, u8 mac[ETH_ALEN]; ssize_t rom_size; struct pch_phub_reg *chip = dev_get_drvdata(dev); + int ret; if (!mac_pton(buf, mac)) return -EINVAL; @@ -644,8 +645,10 @@ static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr, if (!chip->pch_phub_extrom_base_address) return -ENOMEM; - pch_phub_write_gbe_mac_addr(chip, mac); + ret = pch_phub_write_gbe_mac_addr(chip, mac); pci_unmap_rom(chip->pdev, chip->pch_phub_extrom_base_address); + if (ret) + return ret; return count; } diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c index b9e2000969f0..95c894482fdd 100644 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ b/drivers/misc/sgi-xp/xpc_uv.c @@ -240,7 +240,7 @@ xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name, nid = cpu_to_node(cpu); page = alloc_pages_exact_node(nid, - GFP_KERNEL | __GFP_ZERO | GFP_THISNODE, + GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE, pg_order); if (page == NULL) { dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d " diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index afe66571ce0b..21181fa243df 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -24,6 +24,9 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> +#include <linux/list.h> +#include <linux/list_sort.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -36,14 +39,35 @@ struct sram_dev { struct clk *clk; }; +struct sram_reserve { + struct list_head list; + u32 start; + u32 size; +}; + +static int sram_reserve_cmp(void *priv, struct list_head *a, + struct list_head *b) +{ + struct sram_reserve *ra = list_entry(a, struct sram_reserve, list); + struct sram_reserve *rb = list_entry(b, struct sram_reserve, list); + + return ra->start - rb->start; +} + static int sram_probe(struct platform_device *pdev) { void __iomem *virt_base; struct sram_dev *sram; struct resource *res; - unsigned long size; + struct device_node *np = pdev->dev.of_node, *child; + unsigned long size, cur_start, cur_size; + struct sram_reserve *rblocks, *block; + struct list_head reserve_list; + unsigned int nblocks; int ret; + INIT_LIST_HEAD(&reserve_list); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); virt_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(virt_base)) @@ -65,19 +89,106 @@ static int sram_probe(struct platform_device *pdev) if (!sram->pool) return -ENOMEM; - ret = gen_pool_add_virt(sram->pool, (unsigned long)virt_base, - res->start, size, -1); - if (ret < 0) { - if (sram->clk) - clk_disable_unprepare(sram->clk); - return ret; + /* + * We need an additional block to mark the end of the memory region + * after the reserved blocks from the dt are processed. + */ + nblocks = (np) ? of_get_available_child_count(np) + 1 : 1; + rblocks = kmalloc((nblocks) * sizeof(*rblocks), GFP_KERNEL); + if (!rblocks) { + ret = -ENOMEM; + goto err_alloc; + } + + block = &rblocks[0]; + for_each_available_child_of_node(np, child) { + struct resource child_res; + + ret = of_address_to_resource(child, 0, &child_res); + if (ret < 0) { + dev_err(&pdev->dev, + "could not get address for node %s\n", + child->full_name); + goto err_chunks; + } + + if (child_res.start < res->start || child_res.end > res->end) { + dev_err(&pdev->dev, + "reserved block %s outside the sram area\n", + child->full_name); + ret = -EINVAL; + goto err_chunks; + } + + block->start = child_res.start - res->start; + block->size = resource_size(&child_res); + list_add_tail(&block->list, &reserve_list); + + dev_dbg(&pdev->dev, "found reserved block 0x%x-0x%x\n", + block->start, + block->start + block->size); + + block++; + } + + /* the last chunk marks the end of the region */ + rblocks[nblocks - 1].start = size; + rblocks[nblocks - 1].size = 0; + list_add_tail(&rblocks[nblocks - 1].list, &reserve_list); + + list_sort(NULL, &reserve_list, sram_reserve_cmp); + + cur_start = 0; + + list_for_each_entry(block, &reserve_list, list) { + /* can only happen if sections overlap */ + if (block->start < cur_start) { + dev_err(&pdev->dev, + "block at 0x%x starts after current offset 0x%lx\n", + block->start, cur_start); + ret = -EINVAL; + goto err_chunks; + } + + /* current start is in a reserved block, so continue after it */ + if (block->start == cur_start) { + cur_start = block->start + block->size; + continue; + } + + /* + * allocate the space between the current starting + * address and the following reserved block, or the + * end of the region. + */ + cur_size = block->start - cur_start; + + dev_dbg(&pdev->dev, "adding chunk 0x%lx-0x%lx\n", + cur_start, cur_start + cur_size); + ret = gen_pool_add_virt(sram->pool, + (unsigned long)virt_base + cur_start, + res->start + cur_start, cur_size, -1); + if (ret < 0) + goto err_chunks; + + /* next allocation after this reserved block */ + cur_start = block->start + block->size; } + kfree(rblocks); + platform_set_drvdata(pdev, sram); dev_dbg(&pdev->dev, "SRAM pool: %ld KiB @ 0x%p\n", size / 1024, virt_base); return 0; + +err_chunks: + kfree(rblocks); +err_alloc: + if (sram->clk) + clk_disable_unprepare(sram->clk); + return ret; } static int sram_remove(struct platform_device *pdev) @@ -87,8 +198,6 @@ static int sram_remove(struct platform_device *pdev) if (gen_pool_avail(sram->pool) < gen_pool_size(sram->pool)) dev_dbg(&pdev->dev, "removed while SRAM allocated\n"); - gen_pool_destroy(sram->pool); - if (sram->clk) clk_disable_unprepare(sram->clk); diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 3aed525e55b4..1972d57aadb3 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -22,7 +22,6 @@ #define pr_fmt(fmt) "(stc): " fmt #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/seq_file.h> diff --git a/drivers/misc/ti_dac7512.c b/drivers/misc/ti_dac7512.c index 83da711ce9f1..cb0289b44a17 100644 --- a/drivers/misc/ti_dac7512.c +++ b/drivers/misc/ti_dac7512.c @@ -20,7 +20,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/spi/spi.h> #include <linux/of.h> diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index 5bc10fa193de..b00335652e52 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -20,7 +20,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/mutex.h> diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index d35cda06b5e8..e0d5017785e5 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c @@ -383,11 +383,12 @@ static int vmci_enable_msix(struct pci_dev *pdev, vmci_dev->msix_entries[i].vector = i; } - result = pci_enable_msix(pdev, vmci_dev->msix_entries, VMCI_MAX_INTRS); + result = pci_enable_msix_exact(pdev, + vmci_dev->msix_entries, VMCI_MAX_INTRS); if (result == 0) vmci_dev->exclusive_vectors = true; - else if (result > 0) - result = pci_enable_msix(pdev, vmci_dev->msix_entries, 1); + else if (result == -ENOSPC) + result = pci_enable_msix_exact(pdev, vmci_dev->msix_entries, 1); return result; } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 55cd110a49c4..c204b7d1532c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2607,7 +2607,7 @@ int dw_mci_probe(struct dw_mci *host) tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); host->card_workqueue = alloc_workqueue("dw-mci-card", - WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1); + WQ_MEM_RECLAIM, 1); if (!host->card_workqueue) { ret = -ENOMEM; goto err_dmaunmap; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 90ff447bf043..a4bee41ad5cb 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -428,6 +428,7 @@ config MTD_NAND_FSL_IFC tristate "NAND support for Freescale IFC controller" depends on MTD_NAND && FSL_SOC select FSL_IFC + select MEMORY help Various Freescale chips e.g P1010, include a NAND Flash machine with built-in hardware ECC capabilities. diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 90ca7e75d6f0..50d9161c4faf 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -30,7 +30,7 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <linux/mtd/nand_ecc.h> -#include <asm/fsl_ifc.h> +#include <linux/fsl_ifc.h> #define FSL_IFC_V1_1_0 0x01010000 #define ERR_BYTE 0xFF /* Value returned for read diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index d72783dd7b96..c0670237e7a2 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -897,7 +897,7 @@ static void flctl_select_chip(struct mtd_info *mtd, int chipnr) if (!flctl->qos_request) { ret = dev_pm_qos_add_request(&flctl->pdev->dev, &flctl->pm_qos, - DEV_PM_QOS_LATENCY, + DEV_PM_QOS_RESUME_LATENCY, 100); if (ret < 0) dev_err(&flctl->pdev->dev, diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 6d20fbde8d43..dcde56057fe1 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -181,7 +181,7 @@ static inline int __agg_has_partner(struct aggregator *agg) */ static inline void __disable_port(struct port *port) { - bond_set_slave_inactive_flags(port->slave); + bond_set_slave_inactive_flags(port->slave, BOND_SLAVE_NOTIFY_LATER); } /** @@ -193,7 +193,7 @@ static inline void __enable_port(struct port *port) struct slave *slave = port->slave; if ((slave->link == BOND_LINK_UP) && IS_UP(slave->dev)) - bond_set_slave_active_flags(slave); + bond_set_slave_active_flags(slave, BOND_SLAVE_NOTIFY_LATER); } /** @@ -2062,6 +2062,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work) struct list_head *iter; struct slave *slave; struct port *port; + bool should_notify_rtnl = BOND_SLAVE_NOTIFY_LATER; read_lock(&bond->lock); rcu_read_lock(); @@ -2119,8 +2120,19 @@ void bond_3ad_state_machine_handler(struct work_struct *work) } re_arm: + bond_for_each_slave_rcu(bond, slave, iter) { + if (slave->should_notify) { + should_notify_rtnl = BOND_SLAVE_NOTIFY_NOW; + break; + } + } rcu_read_unlock(); read_unlock(&bond->lock); + + if (should_notify_rtnl && rtnl_trylock()) { + bond_slave_state_notify(bond); + rtnl_unlock(); + } queue_delayed_work(bond->wq, &bond->ad_work, ad_delta_in_ticks); } diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index a2c47476804d..e8f133e926aa 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -730,7 +730,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon client_info->ntt = 0; } - if (!vlan_get_tag(skb, &client_info->vlan_id)) + if (vlan_get_tag(skb, &client_info->vlan_id)) client_info->vlan_id = 0; if (!client_info->assigned) { diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 1c6104d3501d..e5628fc725c3 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -829,21 +829,25 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) if (bond_is_lb(bond)) { bond_alb_handle_active_change(bond, new_active); if (old_active) - bond_set_slave_inactive_flags(old_active); + bond_set_slave_inactive_flags(old_active, + BOND_SLAVE_NOTIFY_NOW); if (new_active) - bond_set_slave_active_flags(new_active); + bond_set_slave_active_flags(new_active, + BOND_SLAVE_NOTIFY_NOW); } else { rcu_assign_pointer(bond->curr_active_slave, new_active); } if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) { if (old_active) - bond_set_slave_inactive_flags(old_active); + bond_set_slave_inactive_flags(old_active, + BOND_SLAVE_NOTIFY_NOW); if (new_active) { bool should_notify_peers = false; - bond_set_slave_active_flags(new_active); + bond_set_slave_active_flags(new_active, + BOND_SLAVE_NOTIFY_NOW); if (bond->params.fail_over_mac) bond_do_fail_over_mac(bond, new_active, @@ -1193,6 +1197,11 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) return -EBUSY; } + if (bond_dev == slave_dev) { + pr_err("%s: cannot enslave bond to itself.\n", bond_dev->name); + return -EPERM; + } + /* vlan challenged mutual exclusion */ /* no need to lock since we're protected by rtnl_lock */ if (slave_dev->features & NETIF_F_VLAN_CHALLENGED) { @@ -1463,14 +1472,15 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) switch (bond->params.mode) { case BOND_MODE_ACTIVEBACKUP: - bond_set_slave_inactive_flags(new_slave); + bond_set_slave_inactive_flags(new_slave, + BOND_SLAVE_NOTIFY_NOW); break; case BOND_MODE_8023AD: /* in 802.3ad mode, the internal mechanism * will activate the slaves in the selected * aggregator */ - bond_set_slave_inactive_flags(new_slave); + bond_set_slave_inactive_flags(new_slave, BOND_SLAVE_NOTIFY_NOW); /* if this is the first slave */ if (!prev_slave) { SLAVE_AD_INFO(new_slave).id = 1; @@ -1488,7 +1498,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) case BOND_MODE_TLB: case BOND_MODE_ALB: bond_set_active_slave(new_slave); - bond_set_slave_inactive_flags(new_slave); + bond_set_slave_inactive_flags(new_slave, BOND_SLAVE_NOTIFY_NOW); break; default: pr_debug("This slave is always active in trunk mode\n"); @@ -1654,9 +1664,6 @@ static int __bond_release_one(struct net_device *bond_dev, return -EINVAL; } - /* release the slave from its bond */ - bond->slave_cnt--; - bond_sysfs_slave_del(slave); bond_upper_dev_unlink(bond_dev, slave_dev); @@ -1738,6 +1745,7 @@ static int __bond_release_one(struct net_device *bond_dev, unblock_netpoll_tx(); synchronize_rcu(); + bond->slave_cnt--; if (!bond_has_slaves(bond)) { call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev); @@ -2015,7 +2023,8 @@ static void bond_miimon_commit(struct bonding *bond) if (bond->params.mode == BOND_MODE_ACTIVEBACKUP || bond->params.mode == BOND_MODE_8023AD) - bond_set_slave_inactive_flags(slave); + bond_set_slave_inactive_flags(slave, + BOND_SLAVE_NOTIFY_NOW); pr_info("%s: link status definitely down for interface %s, disabling it\n", bond->dev->name, slave->dev->name); @@ -2562,7 +2571,8 @@ static void bond_ab_arp_commit(struct bonding *bond) slave->link = BOND_LINK_UP; if (bond->current_arp_slave) { bond_set_slave_inactive_flags( - bond->current_arp_slave); + bond->current_arp_slave, + BOND_SLAVE_NOTIFY_NOW); bond->current_arp_slave = NULL; } @@ -2582,7 +2592,8 @@ static void bond_ab_arp_commit(struct bonding *bond) slave->link_failure_count++; slave->link = BOND_LINK_DOWN; - bond_set_slave_inactive_flags(slave); + bond_set_slave_inactive_flags(slave, + BOND_SLAVE_NOTIFY_NOW); pr_info("%s: link status definitely down for interface %s, disabling it\n", bond->dev->name, slave->dev->name); @@ -2615,17 +2626,17 @@ do_failover: /* * Send ARP probes for active-backup mode ARP monitor. + * + * Called with rcu_read_lock hold. */ static bool bond_ab_arp_probe(struct bonding *bond) { struct slave *slave, *before = NULL, *new_slave = NULL, - *curr_arp_slave, *curr_active_slave; + *curr_arp_slave = rcu_dereference(bond->current_arp_slave), + *curr_active_slave = rcu_dereference(bond->curr_active_slave); struct list_head *iter; bool found = false; - - rcu_read_lock(); - curr_arp_slave = rcu_dereference(bond->current_arp_slave); - curr_active_slave = rcu_dereference(bond->curr_active_slave); + bool should_notify_rtnl = BOND_SLAVE_NOTIFY_LATER; if (curr_arp_slave && curr_active_slave) pr_info("PROBE: c_arp %s && cas %s BAD\n", @@ -2634,32 +2645,23 @@ static bool bond_ab_arp_probe(struct bonding *bond) if (curr_active_slave) { bond_arp_send_all(bond, curr_active_slave); - rcu_read_unlock(); - return true; + return should_notify_rtnl; } - rcu_read_unlock(); /* if we don't have a curr_active_slave, search for the next available * backup slave from the current_arp_slave and make it the candidate * for becoming the curr_active_slave */ - if (!rtnl_trylock()) - return false; - /* curr_arp_slave might have gone away */ - curr_arp_slave = ACCESS_ONCE(bond->current_arp_slave); - if (!curr_arp_slave) { - curr_arp_slave = bond_first_slave(bond); - if (!curr_arp_slave) { - rtnl_unlock(); - return true; - } + curr_arp_slave = bond_first_slave_rcu(bond); + if (!curr_arp_slave) + return should_notify_rtnl; } - bond_set_slave_inactive_flags(curr_arp_slave); + bond_set_slave_inactive_flags(curr_arp_slave, BOND_SLAVE_NOTIFY_LATER); - bond_for_each_slave(bond, slave, iter) { + bond_for_each_slave_rcu(bond, slave, iter) { if (!found && !before && IS_UP(slave->dev)) before = slave; @@ -2677,7 +2679,8 @@ static bool bond_ab_arp_probe(struct bonding *bond) if (slave->link_failure_count < UINT_MAX) slave->link_failure_count++; - bond_set_slave_inactive_flags(slave); + bond_set_slave_inactive_flags(slave, + BOND_SLAVE_NOTIFY_LATER); pr_info("%s: backup interface %s is now down.\n", bond->dev->name, slave->dev->name); @@ -2689,26 +2692,31 @@ static bool bond_ab_arp_probe(struct bonding *bond) if (!new_slave && before) new_slave = before; - if (!new_slave) { - rtnl_unlock(); - return true; - } + if (!new_slave) + goto check_state; new_slave->link = BOND_LINK_BACK; - bond_set_slave_active_flags(new_slave); + bond_set_slave_active_flags(new_slave, BOND_SLAVE_NOTIFY_LATER); bond_arp_send_all(bond, new_slave); new_slave->jiffies = jiffies; rcu_assign_pointer(bond->current_arp_slave, new_slave); - rtnl_unlock(); - return true; +check_state: + bond_for_each_slave_rcu(bond, slave, iter) { + if (slave->should_notify) { + should_notify_rtnl = BOND_SLAVE_NOTIFY_NOW; + break; + } + } + return should_notify_rtnl; } static void bond_activebackup_arp_mon(struct work_struct *work) { struct bonding *bond = container_of(work, struct bonding, arp_work.work); - bool should_notify_peers = false, should_commit = false; + bool should_notify_peers = false; + bool should_notify_rtnl = false; int delta_in_ticks; delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval); @@ -2717,11 +2725,12 @@ static void bond_activebackup_arp_mon(struct work_struct *work) goto re_arm; rcu_read_lock(); + should_notify_peers = bond_should_notify_peers(bond); - should_commit = bond_ab_arp_inspect(bond); - rcu_read_unlock(); - if (should_commit) { + if (bond_ab_arp_inspect(bond)) { + rcu_read_unlock(); + /* Race avoidance with bond_close flush of workqueue */ if (!rtnl_trylock()) { delta_in_ticks = 1; @@ -2730,23 +2739,28 @@ static void bond_activebackup_arp_mon(struct work_struct *work) } bond_ab_arp_commit(bond); + rtnl_unlock(); + rcu_read_lock(); } - if (!bond_ab_arp_probe(bond)) { - /* rtnl locking failed, re-arm */ - delta_in_ticks = 1; - should_notify_peers = false; - } + should_notify_rtnl = bond_ab_arp_probe(bond); + rcu_read_unlock(); re_arm: if (bond->params.arp_interval) queue_delayed_work(bond->wq, &bond->arp_work, delta_in_ticks); - if (should_notify_peers) { + if (should_notify_peers || should_notify_rtnl) { if (!rtnl_trylock()) return; - call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, bond->dev); + + if (should_notify_peers) + call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, + bond->dev); + if (should_notify_rtnl) + bond_slave_state_notify(bond); + rtnl_unlock(); } } @@ -3046,9 +3060,11 @@ static int bond_open(struct net_device *bond_dev) bond_for_each_slave(bond, slave, iter) { if ((bond->params.mode == BOND_MODE_ACTIVEBACKUP) && (slave != bond->curr_active_slave)) { - bond_set_slave_inactive_flags(slave); + bond_set_slave_inactive_flags(slave, + BOND_SLAVE_NOTIFY_NOW); } else { - bond_set_slave_active_flags(slave); + bond_set_slave_active_flags(slave, + BOND_SLAVE_NOTIFY_NOW); } } read_unlock(&bond->curr_slave_lock); diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index c37878432717..298c26509095 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -121,6 +121,7 @@ static struct bond_opt_value bond_resend_igmp_tbl[] = { static struct bond_opt_value bond_lp_interval_tbl[] = { { "minval", 1, BOND_VALFLAG_MIN | BOND_VALFLAG_DEFAULT}, { "maxval", INT_MAX, BOND_VALFLAG_MAX}, + { NULL, -1, 0}, }; static struct bond_option bond_opts[] = { diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 86ccfb9f71cc..2b0fdec695f7 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -195,7 +195,8 @@ struct slave { s8 new_link; u8 backup:1, /* indicates backup slave. Value corresponds with BOND_STATE_ACTIVE and BOND_STATE_BACKUP */ - inactive:1; /* indicates inactive slave */ + inactive:1, /* indicates inactive slave */ + should_notify:1; /* indicateds whether the state changed */ u8 duplex; u32 original_mtu; u32 link_failure_count; @@ -303,6 +304,24 @@ static inline void bond_set_backup_slave(struct slave *slave) } } +static inline void bond_set_slave_state(struct slave *slave, + int slave_state, bool notify) +{ + if (slave->backup == slave_state) + return; + + slave->backup = slave_state; + if (notify) { + rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_KERNEL); + slave->should_notify = 0; + } else { + if (slave->should_notify) + slave->should_notify = 0; + else + slave->should_notify = 1; + } +} + static inline void bond_slave_state_change(struct bonding *bond) { struct list_head *iter; @@ -316,6 +335,19 @@ static inline void bond_slave_state_change(struct bonding *bond) } } +static inline void bond_slave_state_notify(struct bonding *bond) +{ + struct list_head *iter; + struct slave *tmp; + + bond_for_each_slave(bond, tmp, iter) { + if (tmp->should_notify) { + rtmsg_ifinfo(RTM_NEWLINK, tmp->dev, 0, GFP_KERNEL); + tmp->should_notify = 0; + } + } +} + static inline int bond_slave_state(struct slave *slave) { return slave->backup; @@ -343,6 +375,9 @@ static inline bool bond_is_active_slave(struct slave *slave) #define BOND_ARP_VALIDATE_ALL (BOND_ARP_VALIDATE_ACTIVE | \ BOND_ARP_VALIDATE_BACKUP) +#define BOND_SLAVE_NOTIFY_NOW true +#define BOND_SLAVE_NOTIFY_LATER false + static inline int slave_do_arp_validate(struct bonding *bond, struct slave *slave) { @@ -394,17 +429,19 @@ static inline void bond_netpoll_send_skb(const struct slave *slave, } #endif -static inline void bond_set_slave_inactive_flags(struct slave *slave) +static inline void bond_set_slave_inactive_flags(struct slave *slave, + bool notify) { if (!bond_is_lb(slave->bond)) - bond_set_backup_slave(slave); + bond_set_slave_state(slave, BOND_STATE_BACKUP, notify); if (!slave->bond->params.all_slaves_active) slave->inactive = 1; } -static inline void bond_set_slave_active_flags(struct slave *slave) +static inline void bond_set_slave_active_flags(struct slave *slave, + bool notify) { - bond_set_active_slave(slave); + bond_set_slave_state(slave, BOND_STATE_ACTIVE, notify); slave->inactive = 0; } diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 320bef2dba42..61376abdab39 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -144,6 +144,8 @@ #define FLEXCAN_MB_CODE_MASK (0xf0ffffff) +#define FLEXCAN_TIMEOUT_US (50) + /* * FLEXCAN hardware feature flags * @@ -262,6 +264,22 @@ static inline void flexcan_write(u32 val, void __iomem *addr) } #endif +static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv) +{ + if (!priv->reg_xceiver) + return 0; + + return regulator_enable(priv->reg_xceiver); +} + +static inline int flexcan_transceiver_disable(const struct flexcan_priv *priv) +{ + if (!priv->reg_xceiver) + return 0; + + return regulator_disable(priv->reg_xceiver); +} + static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv, u32 reg_esr) { @@ -269,26 +287,95 @@ static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv, (reg_esr & FLEXCAN_ESR_ERR_BUS); } -static inline void flexcan_chip_enable(struct flexcan_priv *priv) +static int flexcan_chip_enable(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->base; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; reg = flexcan_read(®s->mcr); reg &= ~FLEXCAN_MCR_MDIS; flexcan_write(reg, ®s->mcr); - udelay(10); + while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + usleep_range(10, 20); + + if (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK) + return -ETIMEDOUT; + + return 0; } -static inline void flexcan_chip_disable(struct flexcan_priv *priv) +static int flexcan_chip_disable(struct flexcan_priv *priv) { struct flexcan_regs __iomem *regs = priv->base; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; u32 reg; reg = flexcan_read(®s->mcr); reg |= FLEXCAN_MCR_MDIS; flexcan_write(reg, ®s->mcr); + + while (timeout-- && !(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + usleep_range(10, 20); + + if (!(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)) + return -ETIMEDOUT; + + return 0; +} + +static int flexcan_chip_freeze(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->base; + unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate; + u32 reg; + + reg = flexcan_read(®s->mcr); + reg |= FLEXCAN_MCR_HALT; + flexcan_write(reg, ®s->mcr); + + while (timeout-- && !(flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + usleep_range(100, 200); + + if (!(flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + return -ETIMEDOUT; + + return 0; +} + +static int flexcan_chip_unfreeze(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->base; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + u32 reg; + + reg = flexcan_read(®s->mcr); + reg &= ~FLEXCAN_MCR_HALT; + flexcan_write(reg, ®s->mcr); + + while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)) + usleep_range(10, 20); + + if (flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK) + return -ETIMEDOUT; + + return 0; +} + +static int flexcan_chip_softreset(struct flexcan_priv *priv) +{ + struct flexcan_regs __iomem *regs = priv->base; + unsigned int timeout = FLEXCAN_TIMEOUT_US / 10; + + flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr); + while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_SOFTRST)) + usleep_range(10, 20); + + if (flexcan_read(®s->mcr) & FLEXCAN_MCR_SOFTRST) + return -ETIMEDOUT; + + return 0; } static int flexcan_get_berr_counter(const struct net_device *dev, @@ -709,19 +796,14 @@ static int flexcan_chip_start(struct net_device *dev) u32 reg_mcr, reg_ctrl; /* enable module */ - flexcan_chip_enable(priv); + err = flexcan_chip_enable(priv); + if (err) + return err; /* soft reset */ - flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr); - udelay(10); - - reg_mcr = flexcan_read(®s->mcr); - if (reg_mcr & FLEXCAN_MCR_SOFTRST) { - netdev_err(dev, "Failed to softreset can module (mcr=0x%08x)\n", - reg_mcr); - err = -ENODEV; - goto out; - } + err = flexcan_chip_softreset(priv); + if (err) + goto out_chip_disable; flexcan_set_bittiming(dev); @@ -788,16 +870,14 @@ static int flexcan_chip_start(struct net_device *dev) if (priv->devtype_data->features & FLEXCAN_HAS_V10_FEATURES) flexcan_write(0x0, ®s->rxfgmask); - if (priv->reg_xceiver) { - err = regulator_enable(priv->reg_xceiver); - if (err) - goto out; - } + err = flexcan_transceiver_enable(priv); + if (err) + goto out_chip_disable; /* synchronize with the can bus */ - reg_mcr = flexcan_read(®s->mcr); - reg_mcr &= ~FLEXCAN_MCR_HALT; - flexcan_write(reg_mcr, ®s->mcr); + err = flexcan_chip_unfreeze(priv); + if (err) + goto out_transceiver_disable; priv->can.state = CAN_STATE_ERROR_ACTIVE; @@ -810,7 +890,9 @@ static int flexcan_chip_start(struct net_device *dev) return 0; - out: + out_transceiver_disable: + flexcan_transceiver_disable(priv); + out_chip_disable: flexcan_chip_disable(priv); return err; } @@ -825,18 +907,17 @@ static void flexcan_chip_stop(struct net_device *dev) { struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_regs __iomem *regs = priv->base; - u32 reg; + + /* freeze + disable module */ + flexcan_chip_freeze(priv); + flexcan_chip_disable(priv); /* Disable all interrupts */ flexcan_write(0, ®s->imask1); + flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, + ®s->ctrl); - /* Disable + halt module */ - reg = flexcan_read(®s->mcr); - reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT; - flexcan_write(reg, ®s->mcr); - - if (priv->reg_xceiver) - regulator_disable(priv->reg_xceiver); + flexcan_transceiver_disable(priv); priv->can.state = CAN_STATE_STOPPED; return; @@ -866,7 +947,7 @@ static int flexcan_open(struct net_device *dev) /* start chip and queuing */ err = flexcan_chip_start(dev); if (err) - goto out_close; + goto out_free_irq; can_led_event(dev, CAN_LED_EVENT_OPEN); @@ -875,6 +956,8 @@ static int flexcan_open(struct net_device *dev) return 0; + out_free_irq: + free_irq(dev->irq, dev); out_close: close_candev(dev); out_disable_per: @@ -945,12 +1028,16 @@ static int register_flexcandev(struct net_device *dev) goto out_disable_ipg; /* select "bus clock", chip must be disabled */ - flexcan_chip_disable(priv); + err = flexcan_chip_disable(priv); + if (err) + goto out_disable_per; reg = flexcan_read(®s->ctrl); reg |= FLEXCAN_CTRL_CLK_SRC; flexcan_write(reg, ®s->ctrl); - flexcan_chip_enable(priv); + err = flexcan_chip_enable(priv); + if (err) + goto out_chip_disable; /* set freeze, halt and activate FIFO, restrict register access */ reg = flexcan_read(®s->mcr); @@ -967,14 +1054,15 @@ static int register_flexcandev(struct net_device *dev) if (!(reg & FLEXCAN_MCR_FEN)) { netdev_err(dev, "Could not enable RX FIFO, unsupported core\n"); err = -ENODEV; - goto out_disable_per; + goto out_chip_disable; } err = register_candev(dev); - out_disable_per: /* disable core and turn off clocks */ + out_chip_disable: flexcan_chip_disable(priv); + out_disable_per: clk_disable_unprepare(priv->clk_per); out_disable_ipg: clk_disable_unprepare(priv->clk_ipg); @@ -1104,9 +1192,10 @@ static int flexcan_probe(struct platform_device *pdev) static int flexcan_remove(struct platform_device *pdev) { struct net_device *dev = platform_get_drvdata(pdev); + struct flexcan_priv *priv = netdev_priv(dev); unregister_flexcandev(dev); - + netif_napi_del(&priv->napi); free_candev(dev); return 0; @@ -1117,8 +1206,11 @@ static int flexcan_suspend(struct device *device) { struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); + int err; - flexcan_chip_disable(priv); + err = flexcan_chip_disable(priv); + if (err) + return err; if (netif_running(dev)) { netif_stop_queue(dev); @@ -1139,9 +1231,7 @@ static int flexcan_resume(struct device *device) netif_device_attach(dev); netif_start_queue(dev); } - flexcan_chip_enable(priv); - - return 0; + return flexcan_chip_enable(priv); } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 2e45f6ec1bf0..380d24922049 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -1248,19 +1248,13 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * shared register for the high 32 bits, so only a single, aligned, * 4 GB physical address range can be used for descriptors. */ - if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) && - !dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) { + if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) { dev_dbg(&pdev->dev, "DMA to 64-BIT addresses\n"); } else { - err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (err) { - err = dma_set_coherent_mask(&pdev->dev, - DMA_BIT_MASK(32)); - if (err) { - dev_err(&pdev->dev, - "No usable DMA config, aborting\n"); - goto out_pci_disable; - } + dev_err(&pdev->dev, "No usable DMA config, aborting\n"); + goto out_pci_disable; } } diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c index d5c2d3e912e5..422aab27ea1b 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -2436,7 +2436,7 @@ err_reset: err_register: err_sw_init: err_eeprom: - iounmap(adapter->hw.hw_addr); + pci_iounmap(pdev, adapter->hw.hw_addr); err_init_netdev: err_ioremap: free_netdev(netdev); @@ -2474,7 +2474,7 @@ static void atl1e_remove(struct pci_dev *pdev) unregister_netdev(netdev); atl1e_free_ring_resources(adapter); atl1e_force_ps(&adapter->hw); - iounmap(adapter->hw.hw_addr); + pci_iounmap(pdev, adapter->hw.hw_addr); pci_release_regions(pdev); free_netdev(netdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 1f7b5aa114fa..8a7bf7dad898 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -1484,6 +1484,10 @@ static int b44_open(struct net_device *dev) add_timer(&bp->timer); b44_enable_ints(bp); + + if (bp->flags & B44_FLAG_EXTERNAL_PHY) + phy_start(bp->phydev); + netif_start_queue(dev); out: return err; @@ -1646,6 +1650,9 @@ static int b44_close(struct net_device *dev) netif_stop_queue(dev); + if (bp->flags & B44_FLAG_EXTERNAL_PHY) + phy_stop(bp->phydev); + napi_disable(&bp->napi); del_timer_sync(&bp->timer); @@ -2222,7 +2229,12 @@ static void b44_adjust_link(struct net_device *dev) } if (status_changed) { - b44_check_phy(bp); + u32 val = br32(bp, B44_TX_CTRL); + if (bp->flags & B44_FLAG_FULL_DUPLEX) + val |= TX_CTRL_DUPLEX; + else + val &= ~TX_CTRL_DUPLEX; + bw32(bp, B44_TX_CTRL, val); phy_print_status(phydev); } } diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index cda25ac45b47..6c9e1c9bdeb8 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -2507,6 +2507,7 @@ bnx2_fw_sync(struct bnx2 *bp, u32 msg_data, int ack, int silent) bp->fw_wr_seq++; msg_data |= bp->fw_wr_seq; + bp->fw_last_msg = msg_data; bnx2_shmem_wr(bp, BNX2_DRV_MB, msg_data); @@ -4000,8 +4001,23 @@ bnx2_setup_wol(struct bnx2 *bp) wol_msg = BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL; } - if (!(bp->flags & BNX2_FLAG_NO_WOL)) - bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT3 | wol_msg, 1, 0); + if (!(bp->flags & BNX2_FLAG_NO_WOL)) { + u32 val; + + wol_msg |= BNX2_DRV_MSG_DATA_WAIT3; + if (bp->fw_last_msg || BNX2_CHIP(bp) != BNX2_CHIP_5709) { + bnx2_fw_sync(bp, wol_msg, 1, 0); + return; + } + /* Tell firmware not to power down the PHY yet, otherwise + * the chip will take a long time to respond to MMIO reads. + */ + val = bnx2_shmem_rd(bp, BNX2_PORT_FEATURE); + bnx2_shmem_wr(bp, BNX2_PORT_FEATURE, + val | BNX2_PORT_FEATURE_ASF_ENABLED); + bnx2_fw_sync(bp, wol_msg, 1, 0); + bnx2_shmem_wr(bp, BNX2_PORT_FEATURE, val); + } } @@ -4033,9 +4049,22 @@ bnx2_set_power_state(struct bnx2 *bp, pci_power_t state) if (bp->wol) pci_set_power_state(bp->pdev, PCI_D3hot); - } else { - pci_set_power_state(bp->pdev, PCI_D3hot); + break; + + } + if (!bp->fw_last_msg && BNX2_CHIP(bp) == BNX2_CHIP_5709) { + u32 val; + + /* Tell firmware not to power down the PHY yet, + * otherwise the other port may not respond to + * MMIO reads. + */ + val = bnx2_shmem_rd(bp, BNX2_BC_STATE_CONDITION); + val &= ~BNX2_CONDITION_PM_STATE_MASK; + val |= BNX2_CONDITION_PM_STATE_UNPREP; + bnx2_shmem_wr(bp, BNX2_BC_STATE_CONDITION, val); } + pci_set_power_state(bp->pdev, PCI_D3hot); /* No more memory access after this point until * device is brought back to D0. diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index f1cf2c44e7ed..e341bc366fa5 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -6900,6 +6900,7 @@ struct bnx2 { u16 fw_wr_seq; u16 fw_drv_pulse_wr_seq; + u32 fw_last_msg; int rx_max_ring; int rx_ring_size; @@ -7406,6 +7407,10 @@ struct bnx2_rv2p_fw_file { #define BNX2_CONDITION_MFW_RUN_NCSI 0x00006000 #define BNX2_CONDITION_MFW_RUN_NONE 0x0000e000 #define BNX2_CONDITION_MFW_RUN_MASK 0x0000e000 +#define BNX2_CONDITION_PM_STATE_MASK 0x00030000 +#define BNX2_CONDITION_PM_STATE_FULL 0x00030000 +#define BNX2_CONDITION_PM_STATE_PREP 0x00020000 +#define BNX2_CONDITION_PM_STATE_UNPREP 0x00010000 #define BNX2_BC_STATE_DEBUG_CMD 0x1dc #define BNX2_BC_STATE_BC_DBG_CMD_SIGNATURE 0x42440000 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 66c0df78c3ff..dbcff509dc3f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -3875,7 +3875,9 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) xmit_type); } - /* Add the macs to the parsing BD this is a vf */ + /* Add the macs to the parsing BD if this is a vf or if + * Tx Switching is enabled. + */ if (IS_VF(bp)) { /* override GRE parameters in BD */ bnx2x_set_fw_mac_addr(&pbd_e2->data.mac_addr.src_hi, @@ -3887,6 +3889,11 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) &pbd_e2->data.mac_addr.dst_mid, &pbd_e2->data.mac_addr.dst_lo, eth->h_dest); + } else if (bp->flags & TX_SWITCHING) { + bnx2x_set_fw_mac_addr(&pbd_e2->data.mac_addr.dst_hi, + &pbd_e2->data.mac_addr.dst_mid, + &pbd_e2->data.mac_addr.dst_lo, + eth->h_dest); } SET_FLAG(pbd_e2_parsing_data, diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index fcf9105a5476..09f3fefcbf9c 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -1,6 +1,6 @@ /* cnic.c: Broadcom CNIC core network driver. * - * Copyright (c) 2006-2013 Broadcom Corporation + * Copyright (c) 2006-2014 Broadcom Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -342,7 +342,7 @@ static int cnic_send_nlmsg(struct cnic_local *cp, u32 type, while (retry < 3) { rc = 0; rcu_read_lock(); - ulp_ops = rcu_dereference(cnic_ulp_tbl[CNIC_ULP_ISCSI]); + ulp_ops = rcu_dereference(cp->ulp_ops[CNIC_ULP_ISCSI]); if (ulp_ops) rc = ulp_ops->iscsi_nl_send_msg( cp->ulp_handle[CNIC_ULP_ISCSI], @@ -726,7 +726,7 @@ static void cnic_free_dma(struct cnic_dev *dev, struct cnic_dma *dma) for (i = 0; i < dma->num_pages; i++) { if (dma->pg_arr[i]) { - dma_free_coherent(&dev->pcidev->dev, BNX2_PAGE_SIZE, + dma_free_coherent(&dev->pcidev->dev, CNIC_PAGE_SIZE, dma->pg_arr[i], dma->pg_map_arr[i]); dma->pg_arr[i] = NULL; } @@ -785,7 +785,7 @@ static int cnic_alloc_dma(struct cnic_dev *dev, struct cnic_dma *dma, for (i = 0; i < pages; i++) { dma->pg_arr[i] = dma_alloc_coherent(&dev->pcidev->dev, - BNX2_PAGE_SIZE, + CNIC_PAGE_SIZE, &dma->pg_map_arr[i], GFP_ATOMIC); if (dma->pg_arr[i] == NULL) @@ -794,8 +794,8 @@ static int cnic_alloc_dma(struct cnic_dev *dev, struct cnic_dma *dma, if (!use_pg_tbl) return 0; - dma->pgtbl_size = ((pages * 8) + BNX2_PAGE_SIZE - 1) & - ~(BNX2_PAGE_SIZE - 1); + dma->pgtbl_size = ((pages * 8) + CNIC_PAGE_SIZE - 1) & + ~(CNIC_PAGE_SIZE - 1); dma->pgtbl = dma_alloc_coherent(&dev->pcidev->dev, dma->pgtbl_size, &dma->pgtbl_map, GFP_ATOMIC); if (dma->pgtbl == NULL) @@ -900,8 +900,8 @@ static int cnic_alloc_context(struct cnic_dev *dev) if (BNX2_CHIP(cp) == BNX2_CHIP_5709) { int i, k, arr_size; - cp->ctx_blk_size = BNX2_PAGE_SIZE; - cp->cids_per_blk = BNX2_PAGE_SIZE / 128; + cp->ctx_blk_size = CNIC_PAGE_SIZE; + cp->cids_per_blk = CNIC_PAGE_SIZE / 128; arr_size = BNX2_MAX_CID / cp->cids_per_blk * sizeof(struct cnic_ctx); cp->ctx_arr = kzalloc(arr_size, GFP_KERNEL); @@ -933,7 +933,7 @@ static int cnic_alloc_context(struct cnic_dev *dev) for (i = 0; i < cp->ctx_blks; i++) { cp->ctx_arr[i].ctx = dma_alloc_coherent(&dev->pcidev->dev, - BNX2_PAGE_SIZE, + CNIC_PAGE_SIZE, &cp->ctx_arr[i].mapping, GFP_KERNEL); if (cp->ctx_arr[i].ctx == NULL) @@ -1013,7 +1013,7 @@ static int __cnic_alloc_uio_rings(struct cnic_uio_dev *udev, int pages) if (udev->l2_ring) return 0; - udev->l2_ring_size = pages * BNX2_PAGE_SIZE; + udev->l2_ring_size = pages * CNIC_PAGE_SIZE; udev->l2_ring = dma_alloc_coherent(&udev->pdev->dev, udev->l2_ring_size, &udev->l2_ring_map, GFP_KERNEL | __GFP_COMP); @@ -1021,7 +1021,7 @@ static int __cnic_alloc_uio_rings(struct cnic_uio_dev *udev, int pages) return -ENOMEM; udev->l2_buf_size = (cp->l2_rx_ring_size + 1) * cp->l2_single_buf_size; - udev->l2_buf_size = PAGE_ALIGN(udev->l2_buf_size); + udev->l2_buf_size = CNIC_PAGE_ALIGN(udev->l2_buf_size); udev->l2_buf = dma_alloc_coherent(&udev->pdev->dev, udev->l2_buf_size, &udev->l2_buf_map, GFP_KERNEL | __GFP_COMP); @@ -1102,7 +1102,7 @@ static int cnic_init_uio(struct cnic_dev *dev) uinfo->mem[0].size = MB_GET_CID_ADDR(TX_TSS_CID + TX_MAX_TSS_RINGS + 1); uinfo->mem[1].addr = (unsigned long) cp->status_blk.gen & - PAGE_MASK; + CNIC_PAGE_MASK; if (cp->ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) uinfo->mem[1].size = BNX2_SBLK_MSIX_ALIGN_SIZE * 9; else @@ -1113,7 +1113,7 @@ static int cnic_init_uio(struct cnic_dev *dev) uinfo->mem[0].size = pci_resource_len(dev->pcidev, 0); uinfo->mem[1].addr = (unsigned long) cp->bnx2x_def_status_blk & - PAGE_MASK; + CNIC_PAGE_MASK; uinfo->mem[1].size = sizeof(*cp->bnx2x_def_status_blk); uinfo->name = "bnx2x_cnic"; @@ -1267,14 +1267,14 @@ static int cnic_alloc_bnx2x_resc(struct cnic_dev *dev) for (i = MAX_ISCSI_TBL_SZ; i < cp->max_cid_space; i++) cp->ctx_tbl[i].ulp_proto_id = CNIC_ULP_FCOE; - pages = PAGE_ALIGN(cp->max_cid_space * CNIC_KWQ16_DATA_SIZE) / - PAGE_SIZE; + pages = CNIC_PAGE_ALIGN(cp->max_cid_space * CNIC_KWQ16_DATA_SIZE) / + CNIC_PAGE_SIZE; ret = cnic_alloc_dma(dev, kwq_16_dma, pages, 0); if (ret) return -ENOMEM; - n = PAGE_SIZE / CNIC_KWQ16_DATA_SIZE; + n = CNIC_PAGE_SIZE / CNIC_KWQ16_DATA_SIZE; for (i = 0, j = 0; i < cp->max_cid_space; i++) { long off = CNIC_KWQ16_DATA_SIZE * (i % n); @@ -1296,7 +1296,7 @@ static int cnic_alloc_bnx2x_resc(struct cnic_dev *dev) goto error; } - pages = PAGE_ALIGN(BNX2X_ISCSI_GLB_BUF_SIZE) / PAGE_SIZE; + pages = CNIC_PAGE_ALIGN(BNX2X_ISCSI_GLB_BUF_SIZE) / CNIC_PAGE_SIZE; ret = cnic_alloc_dma(dev, &cp->gbl_buf_info, pages, 0); if (ret) goto error; @@ -1466,8 +1466,8 @@ static int cnic_bnx2x_iscsi_init1(struct cnic_dev *dev, struct kwqe *kwqe) cp->r2tq_size = cp->num_iscsi_tasks * BNX2X_ISCSI_MAX_PENDING_R2TS * BNX2X_ISCSI_R2TQE_SIZE; cp->hq_size = cp->num_ccells * BNX2X_ISCSI_HQ_BD_SIZE; - pages = PAGE_ALIGN(cp->hq_size) / PAGE_SIZE; - hq_bds = pages * (PAGE_SIZE / BNX2X_ISCSI_HQ_BD_SIZE); + pages = CNIC_PAGE_ALIGN(cp->hq_size) / CNIC_PAGE_SIZE; + hq_bds = pages * (CNIC_PAGE_SIZE / BNX2X_ISCSI_HQ_BD_SIZE); cp->num_cqs = req1->num_cqs; if (!dev->max_iscsi_conn) @@ -1477,9 +1477,9 @@ static int cnic_bnx2x_iscsi_init1(struct cnic_dev *dev, struct kwqe *kwqe) CNIC_WR16(dev, BAR_TSTRORM_INTMEM + TSTORM_ISCSI_RQ_SIZE_OFFSET(pfid), req1->rq_num_wqes); CNIC_WR16(dev, BAR_TSTRORM_INTMEM + TSTORM_ISCSI_PAGE_SIZE_OFFSET(pfid), - PAGE_SIZE); + CNIC_PAGE_SIZE); CNIC_WR8(dev, BAR_TSTRORM_INTMEM + - TSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfid), PAGE_SHIFT); + TSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfid), CNIC_PAGE_BITS); CNIC_WR16(dev, BAR_TSTRORM_INTMEM + TSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfid), req1->num_tasks_per_conn); @@ -1489,9 +1489,9 @@ static int cnic_bnx2x_iscsi_init1(struct cnic_dev *dev, struct kwqe *kwqe) USTORM_ISCSI_RQ_BUFFER_SIZE_OFFSET(pfid), req1->rq_buffer_size); CNIC_WR16(dev, BAR_USTRORM_INTMEM + USTORM_ISCSI_PAGE_SIZE_OFFSET(pfid), - PAGE_SIZE); + CNIC_PAGE_SIZE); CNIC_WR8(dev, BAR_USTRORM_INTMEM + - USTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfid), PAGE_SHIFT); + USTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfid), CNIC_PAGE_BITS); CNIC_WR16(dev, BAR_USTRORM_INTMEM + USTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfid), req1->num_tasks_per_conn); @@ -1504,9 +1504,9 @@ static int cnic_bnx2x_iscsi_init1(struct cnic_dev *dev, struct kwqe *kwqe) /* init Xstorm RAM */ CNIC_WR16(dev, BAR_XSTRORM_INTMEM + XSTORM_ISCSI_PAGE_SIZE_OFFSET(pfid), - PAGE_SIZE); + CNIC_PAGE_SIZE); CNIC_WR8(dev, BAR_XSTRORM_INTMEM + - XSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfid), PAGE_SHIFT); + XSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfid), CNIC_PAGE_BITS); CNIC_WR16(dev, BAR_XSTRORM_INTMEM + XSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfid), req1->num_tasks_per_conn); @@ -1519,9 +1519,9 @@ static int cnic_bnx2x_iscsi_init1(struct cnic_dev *dev, struct kwqe *kwqe) /* init Cstorm RAM */ CNIC_WR16(dev, BAR_CSTRORM_INTMEM + CSTORM_ISCSI_PAGE_SIZE_OFFSET(pfid), - PAGE_SIZE); + CNIC_PAGE_SIZE); CNIC_WR8(dev, BAR_CSTRORM_INTMEM + - CSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfid), PAGE_SHIFT); + CSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(pfid), CNIC_PAGE_BITS); CNIC_WR16(dev, BAR_CSTRORM_INTMEM + CSTORM_ISCSI_NUM_OF_TASKS_OFFSET(pfid), req1->num_tasks_per_conn); @@ -1623,18 +1623,18 @@ static int cnic_alloc_bnx2x_conn_resc(struct cnic_dev *dev, u32 l5_cid) } ctx->cid = cid; - pages = PAGE_ALIGN(cp->task_array_size) / PAGE_SIZE; + pages = CNIC_PAGE_ALIGN(cp->task_array_size) / CNIC_PAGE_SIZE; ret = cnic_alloc_dma(dev, &iscsi->task_array_info, pages, 1); if (ret) goto error; - pages = PAGE_ALIGN(cp->r2tq_size) / PAGE_SIZE; + pages = CNIC_PAGE_ALIGN(cp->r2tq_size) / CNIC_PAGE_SIZE; ret = cnic_alloc_dma(dev, &iscsi->r2tq_info, pages, 1); if (ret) goto error; - pages = PAGE_ALIGN(cp->hq_size) / PAGE_SIZE; + pages = CNIC_PAGE_ALIGN(cp->hq_size) / CNIC_PAGE_SIZE; ret = cnic_alloc_dma(dev, &iscsi->hq_info, pages, 1); if (ret) goto error; @@ -1760,7 +1760,7 @@ static int cnic_setup_bnx2x_ctx(struct cnic_dev *dev, struct kwqe *wqes[], ictx->tstorm_st_context.iscsi.hdr_bytes_2_fetch = ISCSI_HEADER_SIZE; /* TSTORM requires the base address of RQ DB & not PTE */ ictx->tstorm_st_context.iscsi.rq_db_phy_addr.lo = - req2->rq_page_table_addr_lo & PAGE_MASK; + req2->rq_page_table_addr_lo & CNIC_PAGE_MASK; ictx->tstorm_st_context.iscsi.rq_db_phy_addr.hi = req2->rq_page_table_addr_hi; ictx->tstorm_st_context.iscsi.iscsi_conn_id = req1->iscsi_conn_id; @@ -1842,7 +1842,7 @@ static int cnic_setup_bnx2x_ctx(struct cnic_dev *dev, struct kwqe *wqes[], /* CSTORM and USTORM initialization is different, CSTORM requires * CQ DB base & not PTE addr */ ictx->cstorm_st_context.cq_db_base.lo = - req1->cq_page_table_addr_lo & PAGE_MASK; + req1->cq_page_table_addr_lo & CNIC_PAGE_MASK; ictx->cstorm_st_context.cq_db_base.hi = req1->cq_page_table_addr_hi; ictx->cstorm_st_context.iscsi_conn_id = req1->iscsi_conn_id; ictx->cstorm_st_context.cq_proc_en_bit_map = (1 << cp->num_cqs) - 1; @@ -2911,7 +2911,7 @@ static int cnic_l2_completion(struct cnic_local *cp) u16 hw_cons, sw_cons; struct cnic_uio_dev *udev = cp->udev; union eth_rx_cqe *cqe, *cqe_ring = (union eth_rx_cqe *) - (udev->l2_ring + (2 * BNX2_PAGE_SIZE)); + (udev->l2_ring + (2 * CNIC_PAGE_SIZE)); u32 cmd; int comp = 0; @@ -3244,7 +3244,8 @@ static int cnic_copy_ulp_stats(struct cnic_dev *dev, int ulp_type) int rc; mutex_lock(&cnic_lock); - ulp_ops = cnic_ulp_tbl_prot(ulp_type); + ulp_ops = rcu_dereference_protected(cp->ulp_ops[ulp_type], + lockdep_is_held(&cnic_lock)); if (ulp_ops && ulp_ops->cnic_get_stats) rc = ulp_ops->cnic_get_stats(cp->ulp_handle[ulp_type]); else @@ -4384,7 +4385,7 @@ static int cnic_setup_5709_context(struct cnic_dev *dev, int valid) u32 idx = cp->ctx_arr[i].cid / cp->cids_per_blk; u32 val; - memset(cp->ctx_arr[i].ctx, 0, BNX2_PAGE_SIZE); + memset(cp->ctx_arr[i].ctx, 0, CNIC_PAGE_SIZE); CNIC_WR(dev, BNX2_CTX_HOST_PAGE_TBL_DATA0, (cp->ctx_arr[i].mapping & 0xffffffff) | valid_bit); @@ -4628,7 +4629,7 @@ static void cnic_init_bnx2_rx_ring(struct cnic_dev *dev) val = BNX2_L2CTX_L2_STATUSB_NUM(sb_id); cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_HOST_BDIDX, val); - rxbd = udev->l2_ring + BNX2_PAGE_SIZE; + rxbd = udev->l2_ring + CNIC_PAGE_SIZE; for (i = 0; i < BNX2_MAX_RX_DESC_CNT; i++, rxbd++) { dma_addr_t buf_map; int n = (i % cp->l2_rx_ring_size) + 1; @@ -4639,11 +4640,11 @@ static void cnic_init_bnx2_rx_ring(struct cnic_dev *dev) rxbd->rx_bd_haddr_hi = (u64) buf_map >> 32; rxbd->rx_bd_haddr_lo = (u64) buf_map & 0xffffffff; } - val = (u64) (ring_map + BNX2_PAGE_SIZE) >> 32; + val = (u64) (ring_map + CNIC_PAGE_SIZE) >> 32; cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_NX_BDHADDR_HI, val); rxbd->rx_bd_haddr_hi = val; - val = (u64) (ring_map + BNX2_PAGE_SIZE) & 0xffffffff; + val = (u64) (ring_map + CNIC_PAGE_SIZE) & 0xffffffff; cnic_ctx_wr(dev, cid_addr, BNX2_L2CTX_NX_BDHADDR_LO, val); rxbd->rx_bd_haddr_lo = val; @@ -4709,10 +4710,10 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev) val = CNIC_RD(dev, BNX2_MQ_CONFIG); val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE; - if (BNX2_PAGE_BITS > 12) + if (CNIC_PAGE_BITS > 12) val |= (12 - 8) << 4; else - val |= (BNX2_PAGE_BITS - 8) << 4; + val |= (CNIC_PAGE_BITS - 8) << 4; CNIC_WR(dev, BNX2_MQ_CONFIG, val); @@ -4742,13 +4743,13 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev) /* Initialize the kernel work queue context. */ val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE | - (BNX2_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ; + (CNIC_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ; cnic_ctx_wr(dev, kwq_cid_addr, L5_KRNLQ_TYPE, val); - val = (BNX2_PAGE_SIZE / sizeof(struct kwqe) - 1) << 16; + val = (CNIC_PAGE_SIZE / sizeof(struct kwqe) - 1) << 16; cnic_ctx_wr(dev, kwq_cid_addr, L5_KRNLQ_QE_SELF_SEQ_MAX, val); - val = ((BNX2_PAGE_SIZE / sizeof(struct kwqe)) << 16) | KWQ_PAGE_CNT; + val = ((CNIC_PAGE_SIZE / sizeof(struct kwqe)) << 16) | KWQ_PAGE_CNT; cnic_ctx_wr(dev, kwq_cid_addr, L5_KRNLQ_PGTBL_NPAGES, val); val = (u32) ((u64) cp->kwq_info.pgtbl_map >> 32); @@ -4768,13 +4769,13 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev) /* Initialize the kernel complete queue context. */ val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE | - (BNX2_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ; + (CNIC_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ; cnic_ctx_wr(dev, kcq_cid_addr, L5_KRNLQ_TYPE, val); - val = (BNX2_PAGE_SIZE / sizeof(struct kcqe) - 1) << 16; + val = (CNIC_PAGE_SIZE / sizeof(struct kcqe) - 1) << 16; cnic_ctx_wr(dev, kcq_cid_addr, L5_KRNLQ_QE_SELF_SEQ_MAX, val); - val = ((BNX2_PAGE_SIZE / sizeof(struct kcqe)) << 16) | KCQ_PAGE_CNT; + val = ((CNIC_PAGE_SIZE / sizeof(struct kcqe)) << 16) | KCQ_PAGE_CNT; cnic_ctx_wr(dev, kcq_cid_addr, L5_KRNLQ_PGTBL_NPAGES, val); val = (u32) ((u64) cp->kcq1.dma.pgtbl_map >> 32); @@ -4918,7 +4919,7 @@ static void cnic_init_bnx2x_tx_ring(struct cnic_dev *dev, u32 cli = cp->ethdev->iscsi_l2_client_id; u32 val; - memset(txbd, 0, BNX2_PAGE_SIZE); + memset(txbd, 0, CNIC_PAGE_SIZE); buf_map = udev->l2_buf_map; for (i = 0; i < BNX2_MAX_TX_DESC_CNT; i += 3, txbd += 3) { @@ -4978,9 +4979,9 @@ static void cnic_init_bnx2x_rx_ring(struct cnic_dev *dev, struct bnx2x *bp = netdev_priv(dev->netdev); struct cnic_uio_dev *udev = cp->udev; struct eth_rx_bd *rxbd = (struct eth_rx_bd *) (udev->l2_ring + - BNX2_PAGE_SIZE); + CNIC_PAGE_SIZE); struct eth_rx_cqe_next_page *rxcqe = (struct eth_rx_cqe_next_page *) - (udev->l2_ring + (2 * BNX2_PAGE_SIZE)); + (udev->l2_ring + (2 * CNIC_PAGE_SIZE)); struct host_sp_status_block *sb = cp->bnx2x_def_status_blk; int i; u32 cli = cp->ethdev->iscsi_l2_client_id; @@ -5004,20 +5005,20 @@ static void cnic_init_bnx2x_rx_ring(struct cnic_dev *dev, rxbd->addr_lo = cpu_to_le32(buf_map & 0xffffffff); } - val = (u64) (ring_map + BNX2_PAGE_SIZE) >> 32; + val = (u64) (ring_map + CNIC_PAGE_SIZE) >> 32; rxbd->addr_hi = cpu_to_le32(val); data->rx.bd_page_base.hi = cpu_to_le32(val); - val = (u64) (ring_map + BNX2_PAGE_SIZE) & 0xffffffff; + val = (u64) (ring_map + CNIC_PAGE_SIZE) & 0xffffffff; rxbd->addr_lo = cpu_to_le32(val); data->rx.bd_page_base.lo = cpu_to_le32(val); rxcqe += BNX2X_MAX_RCQ_DESC_CNT; - val = (u64) (ring_map + (2 * BNX2_PAGE_SIZE)) >> 32; + val = (u64) (ring_map + (2 * CNIC_PAGE_SIZE)) >> 32; rxcqe->addr_hi = cpu_to_le32(val); data->rx.cqe_page_base.hi = cpu_to_le32(val); - val = (u64) (ring_map + (2 * BNX2_PAGE_SIZE)) & 0xffffffff; + val = (u64) (ring_map + (2 * CNIC_PAGE_SIZE)) & 0xffffffff; rxcqe->addr_lo = cpu_to_le32(val); data->rx.cqe_page_base.lo = cpu_to_le32(val); @@ -5265,8 +5266,8 @@ static void cnic_shutdown_rings(struct cnic_dev *dev) msleep(10); } clear_bit(CNIC_LCL_FL_RINGS_INITED, &cp->cnic_local_flags); - rx_ring = udev->l2_ring + BNX2_PAGE_SIZE; - memset(rx_ring, 0, BNX2_PAGE_SIZE); + rx_ring = udev->l2_ring + CNIC_PAGE_SIZE; + memset(rx_ring, 0, CNIC_PAGE_SIZE); } static int cnic_register_netdev(struct cnic_dev *dev) diff --git a/drivers/net/ethernet/broadcom/cnic.h b/drivers/net/ethernet/broadcom/cnic.h index 0d6b13f854d9..d535ae4228b4 100644 --- a/drivers/net/ethernet/broadcom/cnic.h +++ b/drivers/net/ethernet/broadcom/cnic.h @@ -1,6 +1,6 @@ /* cnic.h: Broadcom CNIC core network driver. * - * Copyright (c) 2006-2013 Broadcom Corporation + * Copyright (c) 2006-2014 Broadcom Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/ethernet/broadcom/cnic_defs.h b/drivers/net/ethernet/broadcom/cnic_defs.h index 95a8e4b11c9f..dcbca6997e8f 100644 --- a/drivers/net/ethernet/broadcom/cnic_defs.h +++ b/drivers/net/ethernet/broadcom/cnic_defs.h @@ -1,7 +1,7 @@ /* cnic.c: Broadcom CNIC core network driver. * - * Copyright (c) 2006-2013 Broadcom Corporation + * Copyright (c) 2006-2014 Broadcom Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/ethernet/broadcom/cnic_if.h b/drivers/net/ethernet/broadcom/cnic_if.h index 8cf6b1926069..5f4d5573a73d 100644 --- a/drivers/net/ethernet/broadcom/cnic_if.h +++ b/drivers/net/ethernet/broadcom/cnic_if.h @@ -1,6 +1,6 @@ /* cnic_if.h: Broadcom CNIC core network driver. * - * Copyright (c) 2006-2013 Broadcom Corporation + * Copyright (c) 2006-2014 Broadcom Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,8 +14,8 @@ #include "bnx2x/bnx2x_mfw_req.h" -#define CNIC_MODULE_VERSION "2.5.19" -#define CNIC_MODULE_RELDATE "December 19, 2013" +#define CNIC_MODULE_VERSION "2.5.20" +#define CNIC_MODULE_RELDATE "March 14, 2014" #define CNIC_ULP_RDMA 0 #define CNIC_ULP_ISCSI 1 @@ -24,6 +24,16 @@ #define MAX_CNIC_ULP_TYPE_EXT 3 #define MAX_CNIC_ULP_TYPE 4 +/* Use CPU native page size up to 16K for cnic ring sizes. */ +#if (PAGE_SHIFT > 14) +#define CNIC_PAGE_BITS 14 +#else +#define CNIC_PAGE_BITS PAGE_SHIFT +#endif +#define CNIC_PAGE_SIZE (1 << (CNIC_PAGE_BITS)) +#define CNIC_PAGE_ALIGN(addr) ALIGN(addr, CNIC_PAGE_SIZE) +#define CNIC_PAGE_MASK (~((CNIC_PAGE_SIZE) - 1)) + struct kwqe { u32 kwqe_op_flag; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 3167ed6593b0..70a225c8df5c 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -6843,8 +6843,7 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget) work_mask |= opaque_key; - if ((desc->err_vlan & RXD_ERR_MASK) != 0 && - (desc->err_vlan != RXD_ERR_ODD_NIBBLE_RCVD_MII)) { + if (desc->err_vlan & RXD_ERR_MASK) { drop_it: tg3_recycle_rx(tnapi, tpr, opaque_key, desc_idx, *post_ptr); @@ -17650,8 +17649,6 @@ static int tg3_init_one(struct pci_dev *pdev, tg3_init_bufmgr_config(tp); - features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; - /* 5700 B0 chips do not support checksumming correctly due * to hardware bugs. */ @@ -17683,7 +17680,8 @@ static int tg3_init_one(struct pci_dev *pdev, features |= NETIF_F_TSO_ECN; } - dev->features |= features; + dev->features |= features | NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_RX; dev->vlan_features |= features; /* diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index ef472385bce4..04321e5a356e 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h @@ -2608,7 +2608,11 @@ struct tg3_rx_buffer_desc { #define RXD_ERR_TOO_SMALL 0x00400000 #define RXD_ERR_NO_RESOURCES 0x00800000 #define RXD_ERR_HUGE_FRAME 0x01000000 -#define RXD_ERR_MASK 0xffff0000 + +#define RXD_ERR_MASK (RXD_ERR_BAD_CRC | RXD_ERR_COLLISION | \ + RXD_ERR_LINK_LOST | RXD_ERR_PHY_DECODE | \ + RXD_ERR_MAC_ABRT | RXD_ERR_TOO_SMALL | \ + RXD_ERR_NO_RESOURCES | RXD_ERR_HUGE_FRAME) u32 reserved; u32 opaque; diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c index 1803c3959044..354ae9792bad 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c @@ -1704,7 +1704,7 @@ bfa_flash_sem_get(void __iomem *bar) while (!bfa_raw_sem_get(bar)) { if (--n <= 0) return BFA_STATUS_BADFLASH; - udelay(10000); + mdelay(10); } return BFA_STATUS_OK; } diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index cf64f3d0b60d..4ad1187e82fb 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -707,7 +707,8 @@ bnad_cq_process(struct bnad *bnad, struct bna_ccb *ccb, int budget) else skb_checksum_none_assert(skb); - if (flags & BNA_CQ_EF_VLAN) + if ((flags & BNA_CQ_EF_VLAN) && + (bnad->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(cmpl->vlan_tag)); if (BNAD_RXBUF_IS_SK_BUFF(unmap_q->type)) @@ -2094,7 +2095,9 @@ bnad_init_rx_config(struct bnad *bnad, struct bna_rx_config *rx_config) rx_config->q1_buf_size = BFI_SMALL_RXBUF_SIZE; } - rx_config->vlan_strip_status = BNA_STATUS_T_ENABLED; + rx_config->vlan_strip_status = + (bnad->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) ? + BNA_STATUS_T_ENABLED : BNA_STATUS_T_DISABLED; } static void @@ -3245,11 +3248,6 @@ bnad_set_rx_mode(struct net_device *netdev) BNA_RXMODE_ALLMULTI; bna_rx_mode_set(bnad->rx_info[0].rx, new_mode, mode_mask, NULL); - if (bnad->cfg_flags & BNAD_CF_PROMISC) - bna_rx_vlan_strip_disable(bnad->rx_info[0].rx); - else - bna_rx_vlan_strip_enable(bnad->rx_info[0].rx); - spin_unlock_irqrestore(&bnad->bna_lock, flags); } @@ -3374,6 +3372,27 @@ bnad_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid) return 0; } +static int bnad_set_features(struct net_device *dev, netdev_features_t features) +{ + struct bnad *bnad = netdev_priv(dev); + netdev_features_t changed = features ^ dev->features; + + if ((changed & NETIF_F_HW_VLAN_CTAG_RX) && netif_running(dev)) { + unsigned long flags; + + spin_lock_irqsave(&bnad->bna_lock, flags); + + if (features & NETIF_F_HW_VLAN_CTAG_RX) + bna_rx_vlan_strip_enable(bnad->rx_info[0].rx); + else + bna_rx_vlan_strip_disable(bnad->rx_info[0].rx); + + spin_unlock_irqrestore(&bnad->bna_lock, flags); + } + + return 0; +} + #ifdef CONFIG_NET_POLL_CONTROLLER static void bnad_netpoll(struct net_device *netdev) @@ -3421,6 +3440,7 @@ static const struct net_device_ops bnad_netdev_ops = { .ndo_change_mtu = bnad_change_mtu, .ndo_vlan_rx_add_vid = bnad_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = bnad_vlan_rx_kill_vid, + .ndo_set_features = bnad_set_features, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = bnad_netpoll #endif @@ -3433,14 +3453,14 @@ bnad_netdev_init(struct bnad *bnad, bool using_dac) netdev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_TX; + NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_RX; netdev->vlan_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6; - netdev->features |= netdev->hw_features | - NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; + netdev->features |= netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; if (using_dac) netdev->features |= NETIF_F_HIGHDMA; diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 3190d38e16fb..d0c38e01e99f 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -632,11 +632,16 @@ static void gem_rx_refill(struct macb *bp) "Unable to allocate sk_buff\n"); break; } - bp->rx_skbuff[entry] = skb; /* now fill corresponding descriptor entry */ paddr = dma_map_single(&bp->pdev->dev, skb->data, bp->rx_buffer_size, DMA_FROM_DEVICE); + if (dma_mapping_error(&bp->pdev->dev, paddr)) { + dev_kfree_skb(skb); + break; + } + + bp->rx_skbuff[entry] = skb; if (entry == RX_RING_SIZE - 1) paddr |= MACB_BIT(RX_WRAP); @@ -725,7 +730,7 @@ static int gem_rx(struct macb *bp, int budget) skb_put(skb, len); addr = MACB_BF(RX_WADDR, MACB_BFEXT(RX_WADDR, addr)); dma_unmap_single(&bp->pdev->dev, addr, - len, DMA_FROM_DEVICE); + bp->rx_buffer_size, DMA_FROM_DEVICE); skb->protocol = eth_type_trans(skb, bp->dev); skb_checksum_none_assert(skb); @@ -1036,11 +1041,15 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) } entry = macb_tx_ring_wrap(bp->tx_head); - bp->tx_head++; netdev_vdbg(bp->dev, "Allocated ring entry %u\n", entry); mapping = dma_map_single(&bp->pdev->dev, skb->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(&bp->pdev->dev, mapping)) { + kfree_skb(skb); + goto unlock; + } + bp->tx_head++; tx_skb = &bp->tx_skb[entry]; tx_skb->skb = skb; tx_skb->mapping = mapping; @@ -1066,6 +1075,7 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) if (CIRC_SPACE(bp->tx_head, bp->tx_tail, TX_RING_SIZE) < 1) netif_stop_queue(dev); +unlock: spin_unlock_irqrestore(&bp->lock, flags); return NETDEV_TX_OK; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 43ab35fea48d..34e2488767d9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -6179,6 +6179,7 @@ static struct pci_driver cxgb4_driver = { .id_table = cxgb4_pci_tbl, .probe = init_one, .remove = remove_one, + .shutdown = remove_one, .err_handler = &cxgb4_eeh, }; diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 8d09615da585..05529e273050 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -350,11 +350,13 @@ struct be_drv_stats { u32 roce_drops_crc; }; +/* A vlan-id of 0xFFFF must be used to clear transparent vlan-tagging */ +#define BE_RESET_VLAN_TAG_ID 0xFFFF + struct be_vf_cfg { unsigned char mac_addr[ETH_ALEN]; int if_handle; int pmac_id; - u16 def_vid; u16 vlan_tag; u32 tx_rate; }; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 04ac9c6a0d39..36c80612e21a 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -913,24 +913,14 @@ static int be_ipv6_tx_stall_chk(struct be_adapter *adapter, return BE3_chip(adapter) && be_ipv6_exthdr_check(skb); } -static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, - struct sk_buff *skb, - bool *skip_hw_vlan) +static struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter, + struct sk_buff *skb, + bool *skip_hw_vlan) { struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; unsigned int eth_hdr_len; struct iphdr *ip; - /* Lancer, SH-R ASICs have a bug wherein Packets that are 32 bytes or less - * may cause a transmit stall on that port. So the work-around is to - * pad short packets (<= 32 bytes) to a 36-byte length. - */ - if (unlikely(!BEx_chip(adapter) && skb->len <= 32)) { - if (skb_padto(skb, 36)) - goto tx_drop; - skb->len = 36; - } - /* For padded packets, BE HW modifies tot_len field in IP header * incorrecly when VLAN tag is inserted by HW. * For padded packets, Lancer computes incorrect checksum. @@ -959,7 +949,7 @@ static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, vlan_tx_tag_present(skb)) { skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan); if (unlikely(!skb)) - goto tx_drop; + goto err; } /* HW may lockup when VLAN HW tagging is requested on @@ -981,15 +971,39 @@ static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, be_vlan_tag_tx_chk(adapter, skb)) { skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan); if (unlikely(!skb)) - goto tx_drop; + goto err; } return skb; tx_drop: dev_kfree_skb_any(skb); +err: return NULL; } +static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, + struct sk_buff *skb, + bool *skip_hw_vlan) +{ + /* Lancer, SH-R ASICs have a bug wherein Packets that are 32 bytes or + * less may cause a transmit stall on that port. So the work-around is + * to pad short packets (<= 32 bytes) to a 36-byte length. + */ + if (unlikely(!BEx_chip(adapter) && skb->len <= 32)) { + if (skb_padto(skb, 36)) + return NULL; + skb->len = 36; + } + + if (BEx_chip(adapter) || lancer_chip(adapter)) { + skb = be_lancer_xmit_workarounds(adapter, skb, skip_hw_vlan); + if (!skb) + return NULL; + } + + return skb; +} + static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev) { struct be_adapter *adapter = netdev_priv(netdev); @@ -1157,6 +1171,14 @@ ret: return status; } +static void be_clear_promisc(struct be_adapter *adapter) +{ + adapter->promiscuous = false; + adapter->flags &= ~BE_FLAGS_VLAN_PROMISC; + + be_cmd_rx_filter(adapter, IFF_PROMISC, OFF); +} + static void be_set_rx_mode(struct net_device *netdev) { struct be_adapter *adapter = netdev_priv(netdev); @@ -1170,9 +1192,7 @@ static void be_set_rx_mode(struct net_device *netdev) /* BE was previously in promiscuous mode; disable it */ if (adapter->promiscuous) { - adapter->promiscuous = false; - be_cmd_rx_filter(adapter, IFF_PROMISC, OFF); - + be_clear_promisc(adapter); if (adapter->vlans_added) be_vid_config(adapter); } @@ -1287,24 +1307,20 @@ static int be_set_vf_vlan(struct net_device *netdev, if (vlan || qos) { vlan |= qos << VLAN_PRIO_SHIFT; - if (vf_cfg->vlan_tag != vlan) { - /* If this is new value, program it. Else skip. */ - vf_cfg->vlan_tag = vlan; + if (vf_cfg->vlan_tag != vlan) status = be_cmd_set_hsw_config(adapter, vlan, vf + 1, vf_cfg->if_handle, 0); - } } else { /* Reset Transparent Vlan Tagging. */ - vf_cfg->vlan_tag = 0; - vlan = vf_cfg->def_vid; - status = be_cmd_set_hsw_config(adapter, vlan, vf + 1, - vf_cfg->if_handle, 0); + status = be_cmd_set_hsw_config(adapter, BE_RESET_VLAN_TAG_ID, + vf + 1, vf_cfg->if_handle, 0); } - - if (status) + if (!status) + vf_cfg->vlan_tag = vlan; + else dev_info(&adapter->pdev->dev, - "VLAN %d config on VF %d failed\n", vlan, vf); + "VLAN %d config on VF %d failed\n", vlan, vf); return status; } @@ -3013,11 +3029,11 @@ static int be_vf_setup_init(struct be_adapter *adapter) static int be_vf_setup(struct be_adapter *adapter) { + struct device *dev = &adapter->pdev->dev; struct be_vf_cfg *vf_cfg; - u16 def_vlan, lnk_speed; int status, old_vfs, vf; - struct device *dev = &adapter->pdev->dev; u32 privileges; + u16 lnk_speed; old_vfs = pci_num_vf(adapter->pdev); if (old_vfs) { @@ -3084,12 +3100,6 @@ static int be_vf_setup(struct be_adapter *adapter) if (!status) vf_cfg->tx_rate = lnk_speed; - status = be_cmd_get_hsw_config(adapter, &def_vlan, - vf + 1, vf_cfg->if_handle, NULL); - if (status) - goto err; - vf_cfg->def_vid = def_vlan; - if (!old_vfs) be_cmd_enable_vf(adapter, vf + 1); } diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 903362a7b584..03a351300013 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -389,12 +389,6 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) netdev_err(ndev, "Tx DMA memory map failed\n"); return NETDEV_TX_OK; } - /* Send it on its way. Tell FEC it's ready, interrupt when done, - * it's the last BD of the frame, and to put the CRC on the end. - */ - status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR - | BD_ENET_TX_LAST | BD_ENET_TX_TC); - bdp->cbd_sc = status; if (fep->bufdesc_ex) { @@ -416,6 +410,13 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *ndev) } } + /* Send it on its way. Tell FEC it's ready, interrupt when done, + * it's the last BD of the frame, and to put the CRC on the end. + */ + status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR + | BD_ENET_TX_LAST | BD_ENET_TX_TC); + bdp->cbd_sc = status; + bdp_pre = fec_enet_get_prevdesc(bdp, fep); if ((id_entry->driver_data & FEC_QUIRK_ERR006358) && !(bdp_pre->cbd_sc & BD_ENET_TX_READY)) { @@ -527,13 +528,6 @@ fec_restart(struct net_device *ndev, int duplex) /* Clear any outstanding interrupt. */ writel(0xffc00000, fep->hwp + FEC_IEVENT); - /* Setup multicast filter. */ - set_multicast_list(ndev); -#ifndef CONFIG_M5272 - writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); - writel(0, fep->hwp + FEC_HASH_TABLE_LOW); -#endif - /* Set maximum receive buffer size. */ writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE); @@ -654,6 +648,13 @@ fec_restart(struct net_device *ndev, int duplex) writel(rcntl, fep->hwp + FEC_R_CNTRL); + /* Setup multicast filter. */ + set_multicast_list(ndev); +#ifndef CONFIG_M5272 + writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); + writel(0, fep->hwp + FEC_HASH_TABLE_LOW); +#endif + if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) { /* enable ENET endian swap */ ecntl |= (1 << 8); diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 4be971590461..1fc8334fc181 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -522,10 +522,21 @@ retry: return rc; } +static u64 ibmveth_encode_mac_addr(u8 *mac) +{ + int i; + u64 encoded = 0; + + for (i = 0; i < ETH_ALEN; i++) + encoded = (encoded << 8) | mac[i]; + + return encoded; +} + static int ibmveth_open(struct net_device *netdev) { struct ibmveth_adapter *adapter = netdev_priv(netdev); - u64 mac_address = 0; + u64 mac_address; int rxq_entries = 1; unsigned long lpar_rc; int rc; @@ -579,8 +590,7 @@ static int ibmveth_open(struct net_device *netdev) adapter->rx_queue.num_slots = rxq_entries; adapter->rx_queue.toggle = 1; - memcpy(&mac_address, netdev->dev_addr, netdev->addr_len); - mac_address = mac_address >> 16; + mac_address = ibmveth_encode_mac_addr(netdev->dev_addr); rxq_desc.fields.flags_len = IBMVETH_BUF_VALID | adapter->rx_queue.queue_len; @@ -1183,8 +1193,8 @@ static void ibmveth_set_multicast_list(struct net_device *netdev) /* add the addresses to the filter table */ netdev_for_each_mc_addr(ha, netdev) { /* add the multicast address to the filter table */ - unsigned long mcast_addr = 0; - memcpy(((char *)&mcast_addr)+2, ha->addr, ETH_ALEN); + u64 mcast_addr; + mcast_addr = ibmveth_encode_mac_addr(ha->addr); lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, IbmVethMcastAddFilter, mcast_addr); @@ -1372,9 +1382,6 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) netif_napi_add(netdev, &adapter->napi, ibmveth_poll, 16); - adapter->mac_addr = 0; - memcpy(&adapter->mac_addr, mac_addr_p, ETH_ALEN); - netdev->irq = dev->irq; netdev->netdev_ops = &ibmveth_netdev_ops; netdev->ethtool_ops = &netdev_ethtool_ops; @@ -1383,7 +1390,7 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; netdev->features |= netdev->hw_features; - memcpy(netdev->dev_addr, &adapter->mac_addr, netdev->addr_len); + memcpy(netdev->dev_addr, mac_addr_p, ETH_ALEN); for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) { struct kobject *kobj = &adapter->rx_buff_pool[i].kobj; diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h index 451ba7949e15..1f37499d4398 100644 --- a/drivers/net/ethernet/ibm/ibmveth.h +++ b/drivers/net/ethernet/ibm/ibmveth.h @@ -138,7 +138,6 @@ struct ibmveth_adapter { struct napi_struct napi; struct net_device_stats stats; unsigned int mcastFilterSize; - unsigned long mac_addr; void * buffer_list_addr; void * filter_list_addr; dma_addr_t buffer_list_dma; diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index f418f4f20f94..8d76fca7fde7 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <net/ip.h> #include <net/ipv6.h> +#include <linux/io.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_mdio.h> @@ -88,8 +89,9 @@ #define MVNETA_TX_IN_PRGRS BIT(1) #define MVNETA_TX_FIFO_EMPTY BIT(8) #define MVNETA_RX_MIN_FRAME_SIZE 0x247c -#define MVNETA_SGMII_SERDES_CFG 0x24A0 +#define MVNETA_SERDES_CFG 0x24A0 #define MVNETA_SGMII_SERDES_PROTO 0x0cc7 +#define MVNETA_RGMII_SERDES_PROTO 0x0667 #define MVNETA_TYPE_PRIO 0x24bc #define MVNETA_FORCE_UNI BIT(21) #define MVNETA_TXQ_CMD_1 0x24e4 @@ -161,7 +163,7 @@ #define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc #define MVNETA_GMAC0_PORT_ENABLE BIT(0) #define MVNETA_GMAC_CTRL_2 0x2c08 -#define MVNETA_GMAC2_PSC_ENABLE BIT(3) +#define MVNETA_GMAC2_PCS_ENABLE BIT(3) #define MVNETA_GMAC2_PORT_RGMII BIT(4) #define MVNETA_GMAC2_PORT_RESET BIT(6) #define MVNETA_GMAC_STATUS 0x2c10 @@ -710,35 +712,6 @@ static void mvneta_rxq_bm_disable(struct mvneta_port *pp, mvreg_write(pp, MVNETA_RXQ_CONFIG_REG(rxq->id), val); } - - -/* Sets the RGMII Enable bit (RGMIIEn) in port MAC control register */ -static void mvneta_gmac_rgmii_set(struct mvneta_port *pp, int enable) -{ - u32 val; - - val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); - - if (enable) - val |= MVNETA_GMAC2_PORT_RGMII; - else - val &= ~MVNETA_GMAC2_PORT_RGMII; - - mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); -} - -/* Config SGMII port */ -static void mvneta_port_sgmii_config(struct mvneta_port *pp) -{ - u32 val; - - val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); - val |= MVNETA_GMAC2_PSC_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); - - mvreg_write(pp, MVNETA_SGMII_SERDES_CFG, MVNETA_SGMII_SERDES_PROTO); -} - /* Start the Ethernet port RX and TX activity */ static void mvneta_port_up(struct mvneta_port *pp) { @@ -2756,12 +2729,15 @@ static void mvneta_port_power_up(struct mvneta_port *pp, int phy_mode) mvreg_write(pp, MVNETA_UNIT_INTR_CAUSE, 0); if (phy_mode == PHY_INTERFACE_MODE_SGMII) - mvneta_port_sgmii_config(pp); + mvreg_write(pp, MVNETA_SERDES_CFG, MVNETA_SGMII_SERDES_PROTO); + else + mvreg_write(pp, MVNETA_SERDES_CFG, MVNETA_RGMII_SERDES_PROTO); + + val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); - mvneta_gmac_rgmii_set(pp, 1); + val |= MVNETA_GMAC2_PCS_ENABLE | MVNETA_GMAC2_PORT_RGMII; /* Cancel Port Reset */ - val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); val &= ~MVNETA_GMAC2_PORT_RESET; mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); @@ -2774,6 +2750,7 @@ static void mvneta_port_power_up(struct mvneta_port *pp, int phy_mode) static int mvneta_probe(struct platform_device *pdev) { const struct mbus_dram_target_info *dram_target_info; + struct resource *res; struct device_node *dn = pdev->dev.of_node; struct device_node *phy_node; u32 phy_addr; @@ -2838,9 +2815,15 @@ static int mvneta_probe(struct platform_device *pdev) clk_prepare_enable(pp->clk); - pp->base = of_iomap(dn, 0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENODEV; + goto err_clk; + } + + pp->base = devm_ioremap_resource(&pdev->dev, res); if (pp->base == NULL) { - err = -ENOMEM; + err = PTR_ERR(pp->base); goto err_clk; } @@ -2848,7 +2831,7 @@ static int mvneta_probe(struct platform_device *pdev) pp->stats = alloc_percpu(struct mvneta_pcpu_stats); if (!pp->stats) { err = -ENOMEM; - goto err_unmap; + goto err_clk; } for_each_possible_cpu(cpu) { @@ -2913,8 +2896,6 @@ err_deinit: mvneta_deinit(pp); err_free_stats: free_percpu(pp->stats); -err_unmap: - iounmap(pp->base); err_clk: clk_disable_unprepare(pp->clk); err_free_irq: @@ -2934,7 +2915,6 @@ static int mvneta_remove(struct platform_device *pdev) mvneta_deinit(pp); clk_disable_unprepare(pp->clk); free_percpu(pp->stats); - iounmap(pp->base); irq_dispose_mapping(dev->irq); free_netdev(dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index fad45316200a..84a96f70dfb5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -742,6 +742,14 @@ static int mlx4_en_replace_mac(struct mlx4_en_priv *priv, int qpn, err = mlx4_en_uc_steer_add(priv, new_mac, &qpn, &entry->reg_id); + if (err) + return err; + if (priv->tunnel_reg_id) { + mlx4_flow_detach(priv->mdev->dev, priv->tunnel_reg_id); + priv->tunnel_reg_id = 0; + } + err = mlx4_en_tunnel_steer_add(priv, new_mac, qpn, + &priv->tunnel_reg_id); return err; } } @@ -1792,6 +1800,8 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) mc_list[5] = priv->port; mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list, MLX4_PROT_ETH, mclist->reg_id); + if (mclist->tunnel_reg_id) + mlx4_flow_detach(mdev->dev, mclist->tunnel_reg_id); } mlx4_en_clear_list(dev); list_for_each_entry_safe(mclist, tmp, &priv->curr_list, list) { diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 91b69ff4b4a2..7e2995ecea6f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -129,13 +129,14 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [0] = "RSS support", [1] = "RSS Toeplitz Hash Function support", [2] = "RSS XOR Hash Function support", - [3] = "Device manage flow steering support", + [3] = "Device managed flow steering support", [4] = "Automatic MAC reassignment support", [5] = "Time stamping support", [6] = "VST (control vlan insertion/stripping) support", [7] = "FSM (MAC anti-spoofing) support", [8] = "Dynamic QP updates support", - [9] = "TCP/IP offloads/flow-steering for VXLAN support" + [9] = "Device managed flow steering IPoIB support", + [10] = "TCP/IP offloads/flow-steering for VXLAN support" }; int i; @@ -859,7 +860,7 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_CQ_TS_SUPPORT_OFFSET); /* For guests, disable vxlan tunneling */ - MLX4_GET(field, outbox, QUERY_DEV_CAP_VXLAN); + MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_VXLAN); field &= 0xf7; MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_VXLAN); @@ -869,7 +870,7 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_BF_OFFSET); /* For guests, disable mw type 2 */ - MLX4_GET(bmme_flags, outbox, QUERY_DEV_CAP_BMME_FLAGS_OFFSET); + MLX4_GET(bmme_flags, outbox->buf, QUERY_DEV_CAP_BMME_FLAGS_OFFSET); bmme_flags &= ~MLX4_BMME_FLAG_TYPE_2_WIN; MLX4_PUT(outbox->buf, bmme_flags, QUERY_DEV_CAP_BMME_FLAGS_OFFSET); @@ -883,7 +884,7 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, } /* turn off ipoib managed steering for guests */ - MLX4_GET(field, outbox, QUERY_DEV_CAP_FLOW_STEERING_IPOIB_OFFSET); + MLX4_GET(field, outbox->buf, QUERY_DEV_CAP_FLOW_STEERING_IPOIB_OFFSET); field &= ~0x80; MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_FLOW_STEERING_IPOIB_OFFSET); diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index d711158b0d4b..d413e60071d4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -150,6 +150,8 @@ struct mlx4_port_config { struct pci_dev *pdev; }; +static atomic_t pf_loading = ATOMIC_INIT(0); + int mlx4_check_port_params(struct mlx4_dev *dev, enum mlx4_port_type *port_type) { @@ -749,7 +751,7 @@ static void mlx4_request_modules(struct mlx4_dev *dev) has_eth_port = true; } - if (has_ib_port) + if (has_ib_port || (dev->caps.flags & MLX4_DEV_CAP_FLAG_IBOE)) request_module_nowait(IB_DRV_NAME); if (has_eth_port) request_module_nowait(EN_DRV_NAME); @@ -1407,6 +1409,11 @@ static int mlx4_init_slave(struct mlx4_dev *dev) u32 slave_read; u32 cmd_channel_ver; + if (atomic_read(&pf_loading)) { + mlx4_warn(dev, "PF is not ready. Deferring probe\n"); + return -EPROBE_DEFER; + } + mutex_lock(&priv->cmd.slave_cmd_mutex); priv->cmd.max_cmds = 1; mlx4_warn(dev, "Sending reset\n"); @@ -2319,7 +2326,11 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data) if (num_vfs) { mlx4_warn(dev, "Enabling SR-IOV with %d VFs\n", num_vfs); + + atomic_inc(&pf_loading); err = pci_enable_sriov(pdev, num_vfs); + atomic_dec(&pf_loading); + if (err) { mlx4_err(dev, "Failed to enable SR-IOV, continuing without SR-IOV (err = %d).\n", err); @@ -2670,7 +2681,11 @@ static pci_ers_result_t mlx4_pci_err_detected(struct pci_dev *pdev, static pci_ers_result_t mlx4_pci_slot_reset(struct pci_dev *pdev) { - int ret = __mlx4_init_one(pdev, 0); + const struct pci_device_id *id; + int ret; + + id = pci_match_id(mlx4_pci_table, pdev); + ret = __mlx4_init_one(pdev, id->driver_data); return ret ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED; } @@ -2684,6 +2699,7 @@ static struct pci_driver mlx4_driver = { .name = DRV_NAME, .id_table = mlx4_pci_table, .probe = mlx4_init_one, + .shutdown = mlx4_remove_one, .remove = mlx4_remove_one, .err_handler = &mlx4_err_handler, }; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 6b65f7795215..7aec6c833973 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -51,8 +51,8 @@ #define DRV_NAME "mlx4_core" #define PFX DRV_NAME ": " -#define DRV_VERSION "1.1" -#define DRV_RELDATE "Dec, 2011" +#define DRV_VERSION "2.2-1" +#define DRV_RELDATE "Feb, 2014" #define MLX4_FS_UDP_UC_EN (1 << 1) #define MLX4_FS_TCP_UC_EN (1 << 2) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 9ca223bc90fc..b57e8c87a34e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -57,8 +57,8 @@ #include "en_port.h" #define DRV_NAME "mlx4_en" -#define DRV_VERSION "2.0" -#define DRV_RELDATE "Dec 2011" +#define DRV_VERSION "2.2-1" +#define DRV_RELDATE "Feb 2014" #define MLX4_EN_MSG_LEVEL (NETIF_MSG_LINK | NETIF_MSG_IFDOWN) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index a064f06e0cb8..23b7e2d35a93 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -46,8 +46,8 @@ #include "mlx5_core.h" #define DRIVER_NAME "mlx5_core" -#define DRIVER_VERSION "1.0" -#define DRIVER_RELDATE "June 2013" +#define DRIVER_VERSION "2.2-1" +#define DRIVER_RELDATE "Feb 2014" MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>"); MODULE_DESCRIPTION("Mellanox ConnectX-IB HCA core library"); diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index 727b546a9eb8..e0c92e0e5e1d 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -23,6 +23,7 @@ #include <linux/crc32.h> #include <linux/mii.h> #include <linux/eeprom_93cx6.h> +#include <linux/regulator/consumer.h> #include <linux/spi/spi.h> @@ -83,6 +84,7 @@ union ks8851_tx_hdr { * @rc_rxqcr: Cached copy of KS_RXQCR. * @eeprom_size: Companion eeprom size in Bytes, 0 if no eeprom * @eeprom: 93CX6 EEPROM state for accessing on-board EEPROM. + * @vdd_reg: Optional regulator supplying the chip * * The @lock ensures that the chip is protected when certain operations are * in progress. When the read or write packet transfer is in progress, most @@ -130,6 +132,7 @@ struct ks8851_net { struct spi_transfer spi_xfer2[2]; struct eeprom_93cx6 eeprom; + struct regulator *vdd_reg; }; static int msg_enable; @@ -1414,6 +1417,21 @@ static int ks8851_probe(struct spi_device *spi) ks->spidev = spi; ks->tx_space = 6144; + ks->vdd_reg = regulator_get_optional(&spi->dev, "vdd"); + if (IS_ERR(ks->vdd_reg)) { + ret = PTR_ERR(ks->vdd_reg); + if (ret == -EPROBE_DEFER) + goto err_reg; + } else { + ret = regulator_enable(ks->vdd_reg); + if (ret) { + dev_err(&spi->dev, "regulator enable fail: %d\n", + ret); + goto err_reg_en; + } + } + + mutex_init(&ks->lock); spin_lock_init(&ks->statelock); @@ -1508,8 +1526,14 @@ static int ks8851_probe(struct spi_device *spi) err_netdev: free_irq(ndev->irq, ks); -err_id: err_irq: +err_id: + if (!IS_ERR(ks->vdd_reg)) + regulator_disable(ks->vdd_reg); +err_reg_en: + if (!IS_ERR(ks->vdd_reg)) + regulator_put(ks->vdd_reg); +err_reg: free_netdev(ndev); return ret; } @@ -1523,6 +1547,10 @@ static int ks8851_remove(struct spi_device *spi) unregister_netdev(priv->netdev); free_irq(spi->irq, priv); + if (!IS_ERR(priv->vdd_reg)) { + regulator_disable(priv->vdd_reg); + regulator_put(priv->vdd_reg); + } free_netdev(priv->netdev); return 0; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 4146664d4d6a..27c4f131863b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -340,6 +340,7 @@ int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter) if (qlcnic_sriov_vf_check(adapter)) return -EINVAL; num_msix = 1; + adapter->drv_sds_rings = QLCNIC_SINGLE_RING; adapter->drv_tx_rings = QLCNIC_SINGLE_RING; } } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c index 77f1bce432d2..7d4f54912bad 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c @@ -807,7 +807,7 @@ qlcnic_dcb_get_pg_tc_cfg_tx(struct net_device *netdev, int tc, u8 *prio, !type->tc_param_valid) return; - if (tc < 0 || (tc > QLC_DCB_MAX_TC)) + if (tc < 0 || (tc >= QLC_DCB_MAX_TC)) return; tc_cfg = &type->tc_cfg[tc]; @@ -843,7 +843,7 @@ static void qlcnic_dcb_get_pg_bwg_cfg_tx(struct net_device *netdev, int pgid, !type->tc_param_valid) return; - if (pgid < 0 || pgid > QLC_DCB_MAX_PG) + if (pgid < 0 || pgid >= QLC_DCB_MAX_PG) return; pgcfg = &type->pg_cfg[pgid]; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index ba78c7481fa3..1222865cfb73 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -816,9 +816,10 @@ static int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter) if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) { qlcnic_disable_multi_tx(adapter); + adapter->drv_sds_rings = QLCNIC_SINGLE_RING; err = qlcnic_enable_msi_legacy(adapter); - if (!err) + if (err) return err; } } @@ -3863,7 +3864,7 @@ int qlcnic_validate_rings(struct qlcnic_adapter *adapter, __u32 ring_cnt, strcpy(buf, "Tx"); } - if (!qlcnic_use_msi_x && !qlcnic_use_msi) { + if (!QLCNIC_IS_MSI_FAMILY(adapter)) { netdev_err(netdev, "No RSS/TSS support in INT-x mode\n"); return -EINVAL; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c index 09acf15c3a56..e5277a632671 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c @@ -13,8 +13,6 @@ #define QLC_VF_MIN_TX_RATE 100 #define QLC_VF_MAX_TX_RATE 9999 #define QLC_MAC_OPCODE_MASK 0x7 -#define QLC_MAC_STAR_ADD 6 -#define QLC_MAC_STAR_DEL 7 #define QLC_VF_FLOOD_BIT BIT_16 #define QLC_FLOOD_MODE 0x5 @@ -1206,13 +1204,6 @@ static int qlcnic_sriov_validate_cfg_macvlan(struct qlcnic_adapter *adapter, struct qlcnic_vport *vp = vf->vp; u8 op, new_op; - if (((cmd->req.arg[1] & QLC_MAC_OPCODE_MASK) == QLC_MAC_STAR_ADD) || - ((cmd->req.arg[1] & QLC_MAC_OPCODE_MASK) == QLC_MAC_STAR_DEL)) { - netdev_err(adapter->netdev, "MAC + any VLAN filter not allowed from VF %d\n", - vf->pci_func); - return -EINVAL; - } - if (!(cmd->req.arg[1] & BIT_8)) return -EINVAL; diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index ce2cfddbed50..656c65ddadb4 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4765,7 +4765,9 @@ static int qlge_probe(struct pci_dev *pdev, ndev->features = ndev->hw_features; ndev->vlan_features = ndev->hw_features; /* vlan gets same features (except vlan filter) */ - ndev->vlan_features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; + ndev->vlan_features &= ~(NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_RX); if (test_bit(QL_DMA64, &qdev->flags)) ndev->features |= NETIF_F_HIGHDMA; diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 91a67ae8f17b..3ff7bc3e7a23 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -209,7 +209,7 @@ static const struct { [RTL_GIGA_MAC_VER_16] = _R("RTL8101e", RTL_TD_0, NULL, JUMBO_1K, true), [RTL_GIGA_MAC_VER_17] = - _R("RTL8168b/8111b", RTL_TD_1, NULL, JUMBO_4K, false), + _R("RTL8168b/8111b", RTL_TD_0, NULL, JUMBO_4K, false), [RTL_GIGA_MAC_VER_18] = _R("RTL8168cp/8111cp", RTL_TD_1, NULL, JUMBO_6K, false), [RTL_GIGA_MAC_VER_19] = @@ -7118,6 +7118,8 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } mutex_init(&tp->wk.mutex); + u64_stats_init(&tp->rx_stats.syncp); + u64_stats_init(&tp->tx_stats.syncp); /* Get MAC address */ for (i = 0; i < ETH_ALEN; i++) diff --git a/drivers/net/ethernet/sfc/ptp.c b/drivers/net/ethernet/sfc/ptp.c index eb75fbd11a01..d7a36829649a 100644 --- a/drivers/net/ethernet/sfc/ptp.c +++ b/drivers/net/ethernet/sfc/ptp.c @@ -1668,6 +1668,13 @@ void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev) struct efx_ptp_data *ptp = efx->ptp_data; int code = EFX_QWORD_FIELD(*ev, MCDI_EVENT_CODE); + if (!ptp) { + if (net_ratelimit()) + netif_warn(efx, drv, efx->net_dev, + "Received PTP event but PTP not set up\n"); + return; + } + if (!ptp->enabled) return; diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index 72d282bf33a5..c553f6b5a913 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -151,7 +151,7 @@ static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) sizeof(struct dma_desc))); } -const struct stmmac_chain_mode_ops chain_mode_ops = { +const struct stmmac_mode_ops chain_mode_ops = { .init = stmmac_init_dma_chain, .is_jumbo_frm = stmmac_is_jumbo_frm, .jumbo_frm = stmmac_jumbo_frm, diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 7834a3993946..74610f3aca9e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -419,20 +419,13 @@ struct mii_regs { unsigned int data; /* MII Data */ }; -struct stmmac_ring_mode_ops { - unsigned int (*is_jumbo_frm) (int len, int ehn_desc); - unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum); - void (*refill_desc3) (void *priv, struct dma_desc *p); - void (*init_desc3) (struct dma_desc *p); - void (*clean_desc3) (void *priv, struct dma_desc *p); - int (*set_16kib_bfsize) (int mtu); -}; - -struct stmmac_chain_mode_ops { +struct stmmac_mode_ops { void (*init) (void *des, dma_addr_t phy_addr, unsigned int size, unsigned int extend_desc); unsigned int (*is_jumbo_frm) (int len, int ehn_desc); unsigned int (*jumbo_frm) (void *priv, struct sk_buff *skb, int csum); + int (*set_16kib_bfsize)(int mtu); + void (*init_desc3)(struct dma_desc *p); void (*refill_desc3) (void *priv, struct dma_desc *p); void (*clean_desc3) (void *priv, struct dma_desc *p); }; @@ -441,8 +434,7 @@ struct mac_device_info { const struct stmmac_ops *mac; const struct stmmac_desc_ops *desc; const struct stmmac_dma_ops *dma; - const struct stmmac_ring_mode_ops *ring; - const struct stmmac_chain_mode_ops *chain; + const struct stmmac_mode_ops *mode; const struct stmmac_hwtimestamp *ptp; struct mii_regs mii; /* MII register Addresses */ struct mac_link link; @@ -460,7 +452,7 @@ void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr, void stmmac_set_mac(void __iomem *ioaddr, bool enable); void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr); -extern const struct stmmac_ring_mode_ops ring_mode_ops; -extern const struct stmmac_chain_mode_ops chain_mode_ops; +extern const struct stmmac_mode_ops ring_mode_ops; +extern const struct stmmac_mode_ops chain_mode_ops; #endif /* __COMMON_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index a96c7c2f5f3f..650a4be6bce5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -100,10 +100,9 @@ static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p) { struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; - if (unlikely(priv->plat->has_gmac)) - /* Fill DES3 in case of RING mode */ - if (priv->dma_buf_sz >= BUF_SIZE_8KiB) - p->des3 = p->des2 + BUF_SIZE_8KiB; + /* Fill DES3 in case of RING mode */ + if (priv->dma_buf_sz >= BUF_SIZE_8KiB) + p->des3 = p->des2 + BUF_SIZE_8KiB; } /* In ring mode we need to fill the desc3 because it is used as buffer */ @@ -126,7 +125,7 @@ static int stmmac_set_16kib_bfsize(int mtu) return ret; } -const struct stmmac_ring_mode_ops ring_mode_ops = { +const struct stmmac_mode_ops ring_mode_ops = { .is_jumbo_frm = stmmac_is_jumbo_frm, .jumbo_frm = stmmac_jumbo_frm, .refill_desc3 = stmmac_refill_desc3, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index a2e7d2c96e36..8543e1cfd55e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -92,8 +92,8 @@ static int tc = TC_DEFAULT; module_param(tc, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(tc, "DMA threshold control value"); -#define DMA_BUFFER_SIZE BUF_SIZE_4KiB -static int buf_sz = DMA_BUFFER_SIZE; +#define DEFAULT_BUFSIZE 1536 +static int buf_sz = DEFAULT_BUFSIZE; module_param(buf_sz, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(buf_sz, "DMA buffer size"); @@ -136,8 +136,8 @@ static void stmmac_verify_args(void) dma_rxsize = DMA_RX_SIZE; if (unlikely(dma_txsize < 0)) dma_txsize = DMA_TX_SIZE; - if (unlikely((buf_sz < DMA_BUFFER_SIZE) || (buf_sz > BUF_SIZE_16KiB))) - buf_sz = DMA_BUFFER_SIZE; + if (unlikely((buf_sz < DEFAULT_BUFSIZE) || (buf_sz > BUF_SIZE_16KiB))) + buf_sz = DEFAULT_BUFSIZE; if (unlikely(flow_ctrl > 1)) flow_ctrl = FLOW_AUTO; else if (likely(flow_ctrl < 0)) @@ -286,10 +286,25 @@ bool stmmac_eee_init(struct stmmac_priv *priv) /* MAC core supports the EEE feature. */ if (priv->dma_cap.eee) { + int tx_lpi_timer = priv->tx_lpi_timer; + /* Check if the PHY supports EEE */ - if (phy_init_eee(priv->phydev, 1)) + if (phy_init_eee(priv->phydev, 1)) { + /* To manage at run-time if the EEE cannot be supported + * anymore (for example because the lp caps have been + * changed). + * In that case the driver disable own timers. + */ + if (priv->eee_active) { + pr_debug("stmmac: disable EEE\n"); + del_timer_sync(&priv->eee_ctrl_timer); + priv->hw->mac->set_eee_timer(priv->ioaddr, 0, + tx_lpi_timer); + } + priv->eee_active = 0; goto out; - + } + /* Activate the EEE and start timers */ if (!priv->eee_active) { priv->eee_active = 1; init_timer(&priv->eee_ctrl_timer); @@ -300,13 +315,13 @@ bool stmmac_eee_init(struct stmmac_priv *priv) priv->hw->mac->set_eee_timer(priv->ioaddr, STMMAC_DEFAULT_LIT_LS, - priv->tx_lpi_timer); + tx_lpi_timer); } else /* Set HW EEE according to the speed */ priv->hw->mac->set_eee_pls(priv->ioaddr, priv->phydev->link); - pr_info("stmmac: Energy-Efficient Ethernet initialized\n"); + pr_debug("stmmac: Energy-Efficient Ethernet initialized\n"); ret = true; } @@ -886,10 +901,10 @@ static int stmmac_set_bfsize(int mtu, int bufsize) ret = BUF_SIZE_8KiB; else if (mtu >= BUF_SIZE_2KiB) ret = BUF_SIZE_4KiB; - else if (mtu >= DMA_BUFFER_SIZE) + else if (mtu > DEFAULT_BUFSIZE) ret = BUF_SIZE_2KiB; else - ret = DMA_BUFFER_SIZE; + ret = DEFAULT_BUFSIZE; return ret; } @@ -951,9 +966,9 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, p->des2 = priv->rx_skbuff_dma[i]; - if ((priv->mode == STMMAC_RING_MODE) && + if ((priv->hw->mode->init_desc3) && (priv->dma_buf_sz == BUF_SIZE_16KiB)) - priv->hw->ring->init_desc3(p); + priv->hw->mode->init_desc3(p); return 0; } @@ -984,11 +999,8 @@ static int init_dma_desc_rings(struct net_device *dev) unsigned int bfsize = 0; int ret = -ENOMEM; - /* Set the max buffer size according to the DESC mode - * and the MTU. Note that RING mode allows 16KiB bsize. - */ - if (priv->mode == STMMAC_RING_MODE) - bfsize = priv->hw->ring->set_16kib_bfsize(dev->mtu); + if (priv->hw->mode->set_16kib_bfsize) + bfsize = priv->hw->mode->set_16kib_bfsize(dev->mtu); if (bfsize < BUF_SIZE_16KiB) bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz); @@ -1029,15 +1041,15 @@ static int init_dma_desc_rings(struct net_device *dev) /* Setup the chained descriptor addresses */ if (priv->mode == STMMAC_CHAIN_MODE) { if (priv->extend_desc) { - priv->hw->chain->init(priv->dma_erx, priv->dma_rx_phy, - rxsize, 1); - priv->hw->chain->init(priv->dma_etx, priv->dma_tx_phy, - txsize, 1); + priv->hw->mode->init(priv->dma_erx, priv->dma_rx_phy, + rxsize, 1); + priv->hw->mode->init(priv->dma_etx, priv->dma_tx_phy, + txsize, 1); } else { - priv->hw->chain->init(priv->dma_rx, priv->dma_rx_phy, - rxsize, 0); - priv->hw->chain->init(priv->dma_tx, priv->dma_tx_phy, - txsize, 0); + priv->hw->mode->init(priv->dma_rx, priv->dma_rx_phy, + rxsize, 0); + priv->hw->mode->init(priv->dma_tx, priv->dma_tx_phy, + txsize, 0); } } @@ -1288,7 +1300,7 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) DMA_TO_DEVICE); priv->tx_skbuff_dma[entry] = 0; } - priv->hw->ring->clean_desc3(priv, p); + priv->hw->mode->clean_desc3(priv, p); if (likely(skb != NULL)) { dev_kfree_skb(skb); @@ -1705,7 +1717,7 @@ static int stmmac_open(struct net_device *dev) priv->dma_rx_size = STMMAC_ALIGN(dma_rxsize); priv->dma_buf_sz = STMMAC_ALIGN(buf_sz); - alloc_dma_desc_resources(priv); + ret = alloc_dma_desc_resources(priv); if (ret < 0) { pr_err("%s: DMA descriptors allocation failed\n", __func__); goto dma_desc_error; @@ -1844,6 +1856,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) int nfrags = skb_shinfo(skb)->nr_frags; struct dma_desc *desc, *first; unsigned int nopaged_len = skb_headlen(skb); + unsigned int enh_desc = priv->plat->enh_desc; if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) { if (!netif_queue_stopped(dev)) { @@ -1871,27 +1884,19 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) first = desc; /* To program the descriptors according to the size of the frame */ - if (priv->mode == STMMAC_RING_MODE) { - is_jumbo = priv->hw->ring->is_jumbo_frm(skb->len, - priv->plat->enh_desc); - if (unlikely(is_jumbo)) - entry = priv->hw->ring->jumbo_frm(priv, skb, - csum_insertion); - } else { - is_jumbo = priv->hw->chain->is_jumbo_frm(skb->len, - priv->plat->enh_desc); - if (unlikely(is_jumbo)) - entry = priv->hw->chain->jumbo_frm(priv, skb, - csum_insertion); - } + if (enh_desc) + is_jumbo = priv->hw->mode->is_jumbo_frm(skb->len, enh_desc); + if (likely(!is_jumbo)) { desc->des2 = dma_map_single(priv->device, skb->data, nopaged_len, DMA_TO_DEVICE); priv->tx_skbuff_dma[entry] = desc->des2; priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum_insertion, priv->mode); - } else + } else { desc = first; + entry = priv->hw->mode->jumbo_frm(priv, skb, csum_insertion); + } for (i = 0; i < nfrags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; @@ -2029,7 +2034,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) p->des2 = priv->rx_skbuff_dma[entry]; - priv->hw->ring->refill_desc3(priv, p); + priv->hw->mode->refill_desc3(priv, p); if (netif_msg_rx_status(priv)) pr_debug("\trefill entry #%d\n", entry); @@ -2633,11 +2638,11 @@ static int stmmac_hw_init(struct stmmac_priv *priv) /* To use the chained or ring mode */ if (chain_mode) { - priv->hw->chain = &chain_mode_ops; + priv->hw->mode = &chain_mode_ops; pr_info(" Chain mode enabled\n"); priv->mode = STMMAC_CHAIN_MODE; } else { - priv->hw->ring = &ring_mode_ops; + priv->hw->mode = &ring_mode_ops; pr_info(" Ring mode enabled\n"); priv->mode = STMMAC_RING_MODE; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index c61bc72b8e90..8fb32a80f1c1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -36,7 +36,7 @@ static const struct of_device_id stmmac_dt_ids[] = { #ifdef CONFIG_DWMAC_STI { .compatible = "st,stih415-dwmac", .data = &sti_gmac_data}, { .compatible = "st,stih416-dwmac", .data = &sti_gmac_data}, - { .compatible = "st,stih127-dwmac", .data = &sti_gmac_data}, + { .compatible = "st,stid127-dwmac", .data = &sti_gmac_data}, #endif /* SoC specific glue layers should come before generic bindings */ { .compatible = "st,spear600-gmac"}, diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 651087b5c8da..7d6d8ec676c8 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1164,11 +1164,17 @@ static void cpsw_init_host_port(struct cpsw_priv *priv) static void cpsw_slave_stop(struct cpsw_slave *slave, struct cpsw_priv *priv) { + u32 slave_port; + + slave_port = cpsw_get_slave_port(priv, slave->slave_num); + if (!slave->phy) return; phy_stop(slave->phy); phy_disconnect(slave->phy); slave->phy = NULL; + cpsw_ale_control_set(priv->ale, slave_port, + ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); } static int cpsw_ndo_open(struct net_device *ndev) @@ -2223,10 +2229,6 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_ale_ret; } - if (cpts_register(&pdev->dev, priv->cpts, - data->cpts_clock_mult, data->cpts_clock_shift)) - dev_err(priv->dev, "error registering cpts device\n"); - cpsw_notice(priv, probe, "initialized device (regs %pa, irq %d)\n", &ss_res->start, ndev->irq); diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index 364d0c7952c0..88ef27067bf2 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -355,7 +355,7 @@ int cpdma_ctlr_stop(struct cpdma_ctlr *ctlr) int i; spin_lock_irqsave(&ctlr->lock, flags); - if (ctlr->state != CPDMA_STATE_ACTIVE) { + if (ctlr->state == CPDMA_STATE_TEARDOWN) { spin_unlock_irqrestore(&ctlr->lock, flags); return -EINVAL; } @@ -891,7 +891,7 @@ int cpdma_chan_stop(struct cpdma_chan *chan) unsigned timeout; spin_lock_irqsave(&chan->lock, flags); - if (chan->state != CPDMA_STATE_ACTIVE) { + if (chan->state == CPDMA_STATE_TEARDOWN) { spin_unlock_irqrestore(&chan->lock, flags); return -EINVAL; } diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index cd9b164a0434..8f0e69ce07ca 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1532,9 +1532,9 @@ static int emac_dev_open(struct net_device *ndev) struct device *emac_dev = &ndev->dev; u32 cnt; struct resource *res; - int ret; + int q, m, ret; + int res_num = 0, irq_num = 0; int i = 0; - int k = 0; struct emac_priv *priv = netdev_priv(ndev); pm_runtime_get(&priv->pdev->dev); @@ -1564,15 +1564,24 @@ static int emac_dev_open(struct net_device *ndev) } /* Request IRQ */ + while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, + res_num))) { + for (irq_num = res->start; irq_num <= res->end; irq_num++) { + dev_err(emac_dev, "Request IRQ %d\n", irq_num); + if (request_irq(irq_num, emac_irq, 0, ndev->name, + ndev)) { + dev_err(emac_dev, + "DaVinci EMAC: request_irq() failed\n"); + ret = -EBUSY; - while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) { - for (i = res->start; i <= res->end; i++) { - if (devm_request_irq(&priv->pdev->dev, i, emac_irq, - 0, ndev->name, ndev)) goto rollback; + } } - k++; + res_num++; } + /* prepare counters for rollback in case of an error */ + res_num--; + irq_num--; /* Start/Enable EMAC hardware */ emac_hw_enable(priv); @@ -1639,11 +1648,23 @@ static int emac_dev_open(struct net_device *ndev) return 0; -rollback: - - dev_err(emac_dev, "DaVinci EMAC: devm_request_irq() failed"); - ret = -EBUSY; err: + emac_int_disable(priv); + napi_disable(&priv->napi); + +rollback: + for (q = res_num; q >= 0; q--) { + res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, q); + /* at the first iteration, irq_num is already set to the + * right value + */ + if (q != res_num) + irq_num = res->end; + + for (m = irq_num; m >= res->start; m--) + free_irq(m, ndev); + } + cpdma_ctlr_stop(priv->dma); pm_runtime_put(&priv->pdev->dev); return ret; } @@ -1659,6 +1680,9 @@ err: */ static int emac_dev_stop(struct net_device *ndev) { + struct resource *res; + int i = 0; + int irq_num; struct emac_priv *priv = netdev_priv(ndev); struct device *emac_dev = &ndev->dev; @@ -1674,6 +1698,13 @@ static int emac_dev_stop(struct net_device *ndev) if (priv->phydev) phy_disconnect(priv->phydev); + /* Free IRQ */ + while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i))) { + for (irq_num = res->start; irq_num <= res->end; irq_num++) + free_irq(irq_num, priv->ndev); + i++; + } + if (netif_msg_drv(priv)) dev_notice(emac_dev, "DaVinci EMAC: %s stopped\n", ndev->name); diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index ef312bc6b865..6ac20a6738f4 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -923,7 +923,7 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) { dev_err(&pdev->dev, "32-bit PCI DMA addresses not supported by the card!?\n"); - goto err_out; + goto err_out_pci_disable; } /* sanity check */ @@ -931,7 +931,7 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) (pci_resource_len(pdev, 1) < io_size)) { rc = -EIO; dev_err(&pdev->dev, "Insufficient PCI resources, aborting\n"); - goto err_out; + goto err_out_pci_disable; } pioaddr = pci_resource_start(pdev, 0); @@ -942,7 +942,7 @@ static int rhine_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev = alloc_etherdev(sizeof(struct rhine_private)); if (!dev) { rc = -ENOMEM; - goto err_out; + goto err_out_pci_disable; } SET_NETDEV_DEV(dev, &pdev->dev); @@ -1084,6 +1084,8 @@ err_out_free_res: pci_release_regions(pdev); err_out_free_netdev: free_netdev(dev); +err_out_pci_disable: + pci_disable_device(pdev); err_out: return rc; } diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 7141a1937360..d6fce9750b95 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -442,6 +442,8 @@ static int netvsc_probe(struct hv_device *dev, if (!net) return -ENOMEM; + netif_carrier_off(net); + net_device_ctx = netdev_priv(net); net_device_ctx->device_ctx = dev; hv_set_drvdata(dev, net); @@ -473,6 +475,8 @@ static int netvsc_probe(struct hv_device *dev, pr_err("Unable to register netdev.\n"); rndis_filter_device_remove(dev); free_netdev(net); + } else { + schedule_delayed_work(&net_device_ctx->dwork, 0); } return ret; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 1084e5de3ceb..b54fd257652b 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -243,6 +243,22 @@ static int rndis_filter_send_request(struct rndis_device *dev, return ret; } +static void rndis_set_link_state(struct rndis_device *rdev, + struct rndis_request *request) +{ + u32 link_status; + struct rndis_query_complete *query_complete; + + query_complete = &request->response_msg.msg.query_complete; + + if (query_complete->status == RNDIS_STATUS_SUCCESS && + query_complete->info_buflen == sizeof(u32)) { + memcpy(&link_status, (void *)((unsigned long)query_complete + + query_complete->info_buf_offset), sizeof(u32)); + rdev->link_state = link_status != 0; + } +} + static void rndis_filter_receive_response(struct rndis_device *dev, struct rndis_message *resp) { @@ -272,6 +288,10 @@ static void rndis_filter_receive_response(struct rndis_device *dev, sizeof(struct rndis_message) + RNDIS_EXT_LEN) { memcpy(&request->response_msg, resp, resp->msg_len); + if (request->request_msg.ndis_msg_type == + RNDIS_MSG_QUERY && request->request_msg.msg. + query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS) + rndis_set_link_state(dev, request); } else { netdev_err(ndev, "rndis response buffer overflow " @@ -620,7 +640,6 @@ static int rndis_filter_query_device_link_status(struct rndis_device *dev) ret = rndis_filter_query_device(dev, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, &link_status, &size); - dev->link_state = (link_status != 0) ? true : false; return ret; } diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index ab31544bc254..a30258aad139 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -546,12 +546,12 @@ at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) int rc; unsigned long flags; - spin_lock(&lp->lock); + spin_lock_irqsave(&lp->lock, flags); if (lp->irq_busy) { - spin_unlock(&lp->lock); + spin_unlock_irqrestore(&lp->lock, flags); return -EBUSY; } - spin_unlock(&lp->lock); + spin_unlock_irqrestore(&lp->lock, flags); might_sleep(); @@ -725,10 +725,11 @@ static void at86rf230_irqwork_level(struct work_struct *work) static irqreturn_t at86rf230_isr(int irq, void *data) { struct at86rf230_local *lp = data; + unsigned long flags; - spin_lock(&lp->lock); + spin_lock_irqsave(&lp->lock, flags); lp->irq_busy = 1; - spin_unlock(&lp->lock); + spin_unlock_irqrestore(&lp->lock, flags); schedule_work(&lp->irqwork); diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index c14d39bf32d0..d7b2e947184b 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -180,7 +180,8 @@ static void ifb_setup(struct net_device *dev) dev->tx_queue_len = TX_Q_LIMIT; dev->features |= IFB_FEATURES; - dev->vlan_features |= IFB_FEATURES; + dev->vlan_features |= IFB_FEATURES & ~(NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX); dev->flags |= IFF_NOARP; dev->flags &= ~IFF_MULTICAST; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index a5d21893670d..1831fb7cd017 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -506,6 +506,9 @@ static int macvlan_change_mtu(struct net_device *dev, int new_mtu) static struct lock_class_key macvlan_netdev_xmit_lock_key; static struct lock_class_key macvlan_netdev_addr_lock_key; +#define ALWAYS_ON_FEATURES \ + (NETIF_F_SG | NETIF_F_GEN_CSUM | NETIF_F_GSO_SOFTWARE | NETIF_F_LLTX) + #define MACVLAN_FEATURES \ (NETIF_F_SG | NETIF_F_ALL_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \ NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_GSO_ROBUST | \ @@ -539,7 +542,7 @@ static int macvlan_init(struct net_device *dev) dev->state = (dev->state & ~MACVLAN_STATE_MASK) | (lowerdev->state & MACVLAN_STATE_MASK); dev->features = lowerdev->features & MACVLAN_FEATURES; - dev->features |= NETIF_F_LLTX; + dev->features |= ALWAYS_ON_FEATURES; dev->gso_max_size = lowerdev->gso_max_size; dev->iflink = lowerdev->ifindex; dev->hard_header_len = lowerdev->hard_header_len; @@ -699,7 +702,7 @@ static netdev_features_t macvlan_fix_features(struct net_device *dev, features = netdev_increment_features(vlan->lowerdev->features, features, mask); - features |= NETIF_F_LLTX; + features |= ALWAYS_ON_FEATURES; return features; } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 19c9eca0ef26..76d96b9ebcdb 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -164,9 +164,9 @@ static const struct phy_setting settings[] = { * of that setting. Returns the index of the last setting if * none of the others match. */ -static inline int phy_find_setting(int speed, int duplex) +static inline unsigned int phy_find_setting(int speed, int duplex) { - int idx = 0; + unsigned int idx = 0; while (idx < ARRAY_SIZE(settings) && (settings[idx].speed != speed || settings[idx].duplex != duplex)) @@ -185,7 +185,7 @@ static inline int phy_find_setting(int speed, int duplex) * the mask in features. Returns the index of the last setting * if nothing else matches. */ -static inline int phy_find_valid(int idx, u32 features) +static inline unsigned int phy_find_valid(unsigned int idx, u32 features) { while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features)) idx++; @@ -204,7 +204,7 @@ static inline int phy_find_valid(int idx, u32 features) static void phy_sanitize_settings(struct phy_device *phydev) { u32 features = phydev->supported; - int idx; + unsigned int idx; /* Sanitize settings based on PHY capabilities */ if ((features & SUPPORTED_Autoneg) == 0) @@ -954,7 +954,8 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) (phydev->interface == PHY_INTERFACE_MODE_RGMII))) { int eee_lp, eee_cap, eee_adv; u32 lp, cap, adv; - int idx, status; + int status; + unsigned int idx; /* Read phy status to properly get the right settings */ status = phy_read_status(phydev); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 82514e72b3d8..2f6989b1e0dc 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -683,10 +683,9 @@ EXPORT_SYMBOL(phy_detach); int phy_suspend(struct phy_device *phydev) { struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); - struct ethtool_wolinfo wol; + struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; /* If the device has WOL enabled, we cannot suspend the PHY */ - wol.cmd = ETHTOOL_GWOL; phy_ethtool_get_wol(phydev, &wol); if (wol.wolopts) return -EBUSY; @@ -916,6 +915,8 @@ int genphy_read_status(struct phy_device *phydev) int err; int lpa; int lpagb = 0; + int common_adv; + int common_adv_gb = 0; /* Update the link, but return if there was an error */ err = genphy_update_link(phydev); @@ -937,7 +938,7 @@ int genphy_read_status(struct phy_device *phydev) phydev->lp_advertising = mii_stat1000_to_ethtool_lpa_t(lpagb); - lpagb &= adv << 2; + common_adv_gb = lpagb & adv << 2; } lpa = phy_read(phydev, MII_LPA); @@ -950,25 +951,25 @@ int genphy_read_status(struct phy_device *phydev) if (adv < 0) return adv; - lpa &= adv; + common_adv = lpa & adv; phydev->speed = SPEED_10; phydev->duplex = DUPLEX_HALF; phydev->pause = 0; phydev->asym_pause = 0; - if (lpagb & (LPA_1000FULL | LPA_1000HALF)) { + if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF)) { phydev->speed = SPEED_1000; - if (lpagb & LPA_1000FULL) + if (common_adv_gb & LPA_1000FULL) phydev->duplex = DUPLEX_FULL; - } else if (lpa & (LPA_100FULL | LPA_100HALF)) { + } else if (common_adv & (LPA_100FULL | LPA_100HALF)) { phydev->speed = SPEED_100; - if (lpa & LPA_100FULL) + if (common_adv & LPA_100FULL) phydev->duplex = DUPLEX_FULL; } else - if (lpa & LPA_10FULL) + if (common_adv & LPA_10FULL) phydev->duplex = DUPLEX_FULL; if (phydev->duplex == DUPLEX_FULL) { diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 8fe9cb7d0f72..26f8635b027d 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1686,7 +1686,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) TUN_USER_FEATURES | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX; dev->features = dev->hw_features; - dev->vlan_features = dev->features; + dev->vlan_features = dev->features & + ~(NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX); INIT_LIST_HEAD(&tun->disabled); err = tun_attach(tun, file, false); diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index 433f0a00c683..e2797f1e1b31 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_USB_HSO) += hso.o obj-$(CONFIG_USB_NET_AX8817X) += asix.o asix-y := asix_devices.o asix_common.o ax88172a.o obj-$(CONFIG_USB_NET_AX88179_178A) += ax88179_178a.o -obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o r815x.o +obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o obj-$(CONFIG_USB_NET_CDC_EEM) += cdc_eem.o obj-$(CONFIG_USB_NET_DM9601) += dm9601.o obj-$(CONFIG_USB_NET_SR9700) += sr9700.o diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 955df81a4358..054e59ca6946 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1029,20 +1029,12 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) dev->mii.phy_id = 0x03; dev->mii.supports_gmii = 1; - if (usb_device_no_sg_constraint(dev->udev)) - dev->can_dma_sg = 1; - dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM; dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM; - if (dev->can_dma_sg) { - dev->net->features |= NETIF_F_SG | NETIF_F_TSO; - dev->net->hw_features |= NETIF_F_SG | NETIF_F_TSO; - } - /* Enable checksum offload */ *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; @@ -1395,6 +1387,19 @@ static const struct driver_info ax88178a_info = { .tx_fixup = ax88179_tx_fixup, }; +static const struct driver_info dlink_dub1312_info = { + .description = "D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .status = ax88179_status, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + static const struct driver_info sitecom_info = { .description = "Sitecom USB 3.0 to Gigabit Adapter", .bind = ax88179_bind, @@ -1421,6 +1426,19 @@ static const struct driver_info samsung_info = { .tx_fixup = ax88179_tx_fixup, }; +static const struct driver_info lenovo_info = { + .description = "Lenovo OneLinkDock Gigabit LAN", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .status = ax88179_status, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + static const struct usb_device_id products[] = { { /* ASIX AX88179 10/100/1000 */ @@ -1431,6 +1449,10 @@ static const struct usb_device_id products[] = { USB_DEVICE(0x0b95, 0x178a), .driver_info = (unsigned long)&ax88178a_info, }, { + /* D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter */ + USB_DEVICE(0x2001, 0x4a00), + .driver_info = (unsigned long)&dlink_dub1312_info, +}, { /* Sitecom USB 3.0 to Gigabit Adapter */ USB_DEVICE(0x0df6, 0x0072), .driver_info = (unsigned long)&sitecom_info, @@ -1438,6 +1460,10 @@ static const struct usb_device_id products[] = { /* Samsung USB Ethernet Adapter */ USB_DEVICE(0x04e8, 0xa100), .driver_info = (unsigned long)&samsung_info, +}, { + /* Lenovo OneLinkDock Gigabit LAN */ + USB_DEVICE(0x17ef, 0x304b), + .driver_info = (unsigned long)&lenovo_info, }, { }, }; diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 42e176912c8e..bd363b27e854 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -652,6 +652,13 @@ static const struct usb_device_id products[] = { .driver_info = 0, }, +/* Samsung USB Ethernet Adapters */ +{ + USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, 0xa101, USB_CLASS_COMM, + USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), + .driver_info = 0, +}, + /* WHITELIST!!! * * CDC Ether uses two interfaces, not necessarily consecutive. diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index dbff290ed0e4..d350d2795e10 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -68,7 +68,6 @@ static struct usb_driver cdc_ncm_driver; static int cdc_ncm_setup(struct usbnet *dev) { struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; - struct usb_cdc_ncm_ntb_parameters ncm_parm; u32 val; u8 flags; u8 iface_no; @@ -82,22 +81,22 @@ static int cdc_ncm_setup(struct usbnet *dev) err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS, USB_TYPE_CLASS | USB_DIR_IN |USB_RECIP_INTERFACE, - 0, iface_no, &ncm_parm, - sizeof(ncm_parm)); + 0, iface_no, &ctx->ncm_parm, + sizeof(ctx->ncm_parm)); if (err < 0) { dev_err(&dev->intf->dev, "failed GET_NTB_PARAMETERS\n"); return err; /* GET_NTB_PARAMETERS is required */ } /* read correct set of parameters according to device mode */ - ctx->rx_max = le32_to_cpu(ncm_parm.dwNtbInMaxSize); - ctx->tx_max = le32_to_cpu(ncm_parm.dwNtbOutMaxSize); - ctx->tx_remainder = le16_to_cpu(ncm_parm.wNdpOutPayloadRemainder); - ctx->tx_modulus = le16_to_cpu(ncm_parm.wNdpOutDivisor); - ctx->tx_ndp_modulus = le16_to_cpu(ncm_parm.wNdpOutAlignment); + ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize); + ctx->tx_max = le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize); + ctx->tx_remainder = le16_to_cpu(ctx->ncm_parm.wNdpOutPayloadRemainder); + ctx->tx_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutDivisor); + ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment); /* devices prior to NCM Errata shall set this field to zero */ - ctx->tx_max_datagrams = le16_to_cpu(ncm_parm.wNtbOutMaxDatagrams); - ntb_fmt_supported = le16_to_cpu(ncm_parm.bmNtbFormatsSupported); + ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams); + ntb_fmt_supported = le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported); /* there are some minor differences in NCM and MBIM defaults */ if (cdc_ncm_comm_intf_is_mbim(ctx->control->cur_altsetting)) { @@ -146,7 +145,7 @@ static int cdc_ncm_setup(struct usbnet *dev) } /* inform device about NTB input size changes */ - if (ctx->rx_max != le32_to_cpu(ncm_parm.dwNtbInMaxSize)) { + if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) { __le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max); err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE, @@ -162,14 +161,6 @@ static int cdc_ncm_setup(struct usbnet *dev) dev_dbg(&dev->intf->dev, "Using default maximum transmit length=%d\n", CDC_NCM_NTB_MAX_SIZE_TX); ctx->tx_max = CDC_NCM_NTB_MAX_SIZE_TX; - - /* Adding a pad byte here simplifies the handling in - * cdc_ncm_fill_tx_frame, by making tx_max always - * represent the real skb max size. - */ - if (ctx->tx_max % usb_maxpacket(dev->udev, dev->out, 1) == 0) - ctx->tx_max++; - } /* @@ -439,6 +430,10 @@ advance: goto error2; } + /* initialize data interface */ + if (cdc_ncm_setup(dev)) + goto error2; + /* configure data interface */ temp = usb_set_interface(dev->udev, iface_no, data_altsetting); if (temp) { @@ -453,12 +448,6 @@ advance: goto error2; } - /* initialize data interface */ - if (cdc_ncm_setup(dev)) { - dev_dbg(&intf->dev, "cdc_ncm_setup() failed\n"); - goto error2; - } - usb_set_intfdata(ctx->data, dev); usb_set_intfdata(ctx->control, dev); @@ -475,6 +464,15 @@ advance: dev->hard_mtu = ctx->tx_max; dev->rx_urb_size = ctx->rx_max; + /* cdc_ncm_setup will override dwNtbOutMaxSize if it is + * outside the sane range. Adding a pad byte here if necessary + * simplifies the handling in cdc_ncm_fill_tx_frame, making + * tx_max always represent the real skb max size. + */ + if (ctx->tx_max != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) && + ctx->tx_max % usb_maxpacket(dev->udev, dev->out, 1) == 0) + ctx->tx_max++; + return 0; error2: diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index d89dbe395ad2..adb12f349a61 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -449,9 +449,6 @@ enum rtl8152_flags { #define MCU_TYPE_PLA 0x0100 #define MCU_TYPE_USB 0x0000 -#define REALTEK_USB_DEVICE(vend, prod) \ - USB_DEVICE_INTERFACE_CLASS(vend, prod, USB_CLASS_VENDOR_SPEC) - struct rx_desc { __le32 opts1; #define RX_LEN_MASK 0x7fff @@ -2739,6 +2736,12 @@ static int rtl8152_probe(struct usb_interface *intf, struct net_device *netdev; int ret; + if (udev->actconfig->desc.bConfigurationValue != 1) { + usb_driver_set_configuration(udev, 1); + return -ENODEV; + } + + usb_reset_device(udev); netdev = alloc_etherdev(sizeof(struct r8152)); if (!netdev) { dev_err(&intf->dev, "Out of memory\n"); @@ -2819,9 +2822,9 @@ static void rtl8152_disconnect(struct usb_interface *intf) /* table of devices that work with this driver */ static struct usb_device_id rtl8152_table[] = { - {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8152)}, - {REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8153)}, - {REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, PRODUCT_ID_SAMSUNG)}, + {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8152)}, + {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8153)}, + {USB_DEVICE(VENDOR_ID_SAMSUNG, PRODUCT_ID_SAMSUNG)}, {} }; diff --git a/drivers/net/usb/r815x.c b/drivers/net/usb/r815x.c deleted file mode 100644 index f0a8791b7636..000000000000 --- a/drivers/net/usb/r815x.c +++ /dev/null @@ -1,248 +0,0 @@ -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/mii.h> -#include <linux/usb.h> -#include <linux/usb/cdc.h> -#include <linux/usb/usbnet.h> - -#define RTL815x_REQT_READ 0xc0 -#define RTL815x_REQT_WRITE 0x40 -#define RTL815x_REQ_GET_REGS 0x05 -#define RTL815x_REQ_SET_REGS 0x05 - -#define MCU_TYPE_PLA 0x0100 -#define OCP_BASE 0xe86c -#define BASE_MII 0xa400 - -#define BYTE_EN_DWORD 0xff -#define BYTE_EN_WORD 0x33 -#define BYTE_EN_BYTE 0x11 - -#define R815x_PHY_ID 32 -#define REALTEK_VENDOR_ID 0x0bda - - -static int pla_read_word(struct usb_device *udev, u16 index) -{ - int ret; - u8 shift = index & 2; - __le32 *tmp; - - tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); - if (!tmp) - return -ENOMEM; - - index &= ~3; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - RTL815x_REQ_GET_REGS, RTL815x_REQT_READ, - index, MCU_TYPE_PLA, tmp, sizeof(*tmp), 500); - if (ret < 0) - goto out2; - - ret = __le32_to_cpu(*tmp); - ret >>= (shift * 8); - ret &= 0xffff; - -out2: - kfree(tmp); - return ret; -} - -static int pla_write_word(struct usb_device *udev, u16 index, u32 data) -{ - __le32 *tmp; - u32 mask = 0xffff; - u16 byen = BYTE_EN_WORD; - u8 shift = index & 2; - int ret; - - tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); - if (!tmp) - return -ENOMEM; - - data &= mask; - - if (shift) { - byen <<= shift; - mask <<= (shift * 8); - data <<= (shift * 8); - index &= ~3; - } - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - RTL815x_REQ_GET_REGS, RTL815x_REQT_READ, - index, MCU_TYPE_PLA, tmp, sizeof(*tmp), 500); - if (ret < 0) - goto out3; - - data |= __le32_to_cpu(*tmp) & ~mask; - *tmp = __cpu_to_le32(data); - - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - RTL815x_REQ_SET_REGS, RTL815x_REQT_WRITE, - index, MCU_TYPE_PLA | byen, tmp, sizeof(*tmp), - 500); - -out3: - kfree(tmp); - return ret; -} - -static int ocp_reg_read(struct usbnet *dev, u16 addr) -{ - u16 ocp_base, ocp_index; - int ret; - - ocp_base = addr & 0xf000; - ret = pla_write_word(dev->udev, OCP_BASE, ocp_base); - if (ret < 0) - goto out; - - ocp_index = (addr & 0x0fff) | 0xb000; - ret = pla_read_word(dev->udev, ocp_index); - -out: - return ret; -} - -static int ocp_reg_write(struct usbnet *dev, u16 addr, u16 data) -{ - u16 ocp_base, ocp_index; - int ret; - - ocp_base = addr & 0xf000; - ret = pla_write_word(dev->udev, OCP_BASE, ocp_base); - if (ret < 0) - goto out1; - - ocp_index = (addr & 0x0fff) | 0xb000; - ret = pla_write_word(dev->udev, ocp_index, data); - -out1: - return ret; -} - -static int r815x_mdio_read(struct net_device *netdev, int phy_id, int reg) -{ - struct usbnet *dev = netdev_priv(netdev); - int ret; - - if (phy_id != R815x_PHY_ID) - return -EINVAL; - - if (usb_autopm_get_interface(dev->intf) < 0) - return -ENODEV; - - ret = ocp_reg_read(dev, BASE_MII + reg * 2); - - usb_autopm_put_interface(dev->intf); - return ret; -} - -static -void r815x_mdio_write(struct net_device *netdev, int phy_id, int reg, int val) -{ - struct usbnet *dev = netdev_priv(netdev); - - if (phy_id != R815x_PHY_ID) - return; - - if (usb_autopm_get_interface(dev->intf) < 0) - return; - - ocp_reg_write(dev, BASE_MII + reg * 2, val); - - usb_autopm_put_interface(dev->intf); -} - -static int r8153_bind(struct usbnet *dev, struct usb_interface *intf) -{ - int status; - - status = usbnet_cdc_bind(dev, intf); - if (status < 0) - return status; - - dev->mii.dev = dev->net; - dev->mii.mdio_read = r815x_mdio_read; - dev->mii.mdio_write = r815x_mdio_write; - dev->mii.phy_id_mask = 0x3f; - dev->mii.reg_num_mask = 0x1f; - dev->mii.phy_id = R815x_PHY_ID; - dev->mii.supports_gmii = 1; - - return status; -} - -static int r8152_bind(struct usbnet *dev, struct usb_interface *intf) -{ - int status; - - status = usbnet_cdc_bind(dev, intf); - if (status < 0) - return status; - - dev->mii.dev = dev->net; - dev->mii.mdio_read = r815x_mdio_read; - dev->mii.mdio_write = r815x_mdio_write; - dev->mii.phy_id_mask = 0x3f; - dev->mii.reg_num_mask = 0x1f; - dev->mii.phy_id = R815x_PHY_ID; - dev->mii.supports_gmii = 0; - - return status; -} - -static const struct driver_info r8152_info = { - .description = "RTL8152 ECM Device", - .flags = FLAG_ETHER | FLAG_POINTTOPOINT, - .bind = r8152_bind, - .unbind = usbnet_cdc_unbind, - .status = usbnet_cdc_status, - .manage_power = usbnet_manage_power, -}; - -static const struct driver_info r8153_info = { - .description = "RTL8153 ECM Device", - .flags = FLAG_ETHER | FLAG_POINTTOPOINT, - .bind = r8153_bind, - .unbind = usbnet_cdc_unbind, - .status = usbnet_cdc_status, - .manage_power = usbnet_manage_power, -}; - -static const struct usb_device_id products[] = { -{ - USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8152, USB_CLASS_COMM, - USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &r8152_info, -}, - -{ - USB_DEVICE_AND_INTERFACE_INFO(REALTEK_VENDOR_ID, 0x8153, USB_CLASS_COMM, - USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long) &r8153_info, -}, - - { }, /* END */ -}; -MODULE_DEVICE_TABLE(usb, products); - -static struct usb_driver r815x_driver = { - .name = "r815x", - .id_table = products, - .probe = usbnet_probe, - .disconnect = usbnet_disconnect, - .suspend = usbnet_suspend, - .resume = usbnet_resume, - .reset_resume = usbnet_resume, - .supports_autosuspend = 1, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(r815x_driver); - -MODULE_AUTHOR("Hayes Wang"); -MODULE_DESCRIPTION("Realtek USB ECM device"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index dd10d5817d2a..f9e96c427558 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -752,14 +752,12 @@ EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs); // precondition: never called in_interrupt static void usbnet_terminate_urbs(struct usbnet *dev) { - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup); DECLARE_WAITQUEUE(wait, current); int temp; /* ensure there are no more active urbs */ - add_wait_queue(&unlink_wakeup, &wait); + add_wait_queue(&dev->wait, &wait); set_current_state(TASK_UNINTERRUPTIBLE); - dev->wait = &unlink_wakeup; temp = unlink_urbs(dev, &dev->txq) + unlink_urbs(dev, &dev->rxq); @@ -773,15 +771,14 @@ static void usbnet_terminate_urbs(struct usbnet *dev) "waited for %d urb completions\n", temp); } set_current_state(TASK_RUNNING); - dev->wait = NULL; - remove_wait_queue(&unlink_wakeup, &wait); + remove_wait_queue(&dev->wait, &wait); } int usbnet_stop (struct net_device *net) { struct usbnet *dev = netdev_priv(net); struct driver_info *info = dev->driver_info; - int retval; + int retval, pm; clear_bit(EVENT_DEV_OPEN, &dev->flags); netif_stop_queue (net); @@ -791,6 +788,8 @@ int usbnet_stop (struct net_device *net) net->stats.rx_packets, net->stats.tx_packets, net->stats.rx_errors, net->stats.tx_errors); + /* to not race resume */ + pm = usb_autopm_get_interface(dev->intf); /* allow minidriver to stop correctly (wireless devices to turn off * radio etc) */ if (info->stop) { @@ -817,6 +816,9 @@ int usbnet_stop (struct net_device *net) dev->flags = 0; del_timer_sync (&dev->delay); tasklet_kill (&dev->bh); + if (!pm) + usb_autopm_put_interface(dev->intf); + if (info->manage_power && !test_and_clear_bit(EVENT_NO_RUNTIME_PM, &dev->flags)) info->manage_power(dev, 0); @@ -1437,11 +1439,12 @@ static void usbnet_bh (unsigned long param) /* restart RX again after disabling due to high error rate */ clear_bit(EVENT_RX_KILL, &dev->flags); - // waiting for all pending urbs to complete? - if (dev->wait) { - if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { - wake_up (dev->wait); - } + /* waiting for all pending urbs to complete? + * only then can we forgo submitting anew + */ + if (waitqueue_active(&dev->wait)) { + if (dev->txq.qlen + dev->rxq.qlen + dev->done.qlen == 0) + wake_up_all(&dev->wait); // or are we maybe short a few urbs? } else if (netif_running (dev->net) && @@ -1580,6 +1583,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) dev->driver_name = name; dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK); + init_waitqueue_head(&dev->wait); skb_queue_head_init (&dev->rxq); skb_queue_head_init (&dev->txq); skb_queue_head_init (&dev->done); @@ -1791,9 +1795,10 @@ int usbnet_resume (struct usb_interface *intf) spin_unlock_irq(&dev->txq.lock); if (test_bit(EVENT_DEV_OPEN, &dev->flags)) { - /* handle remote wakeup ASAP */ - if (!dev->wait && - netif_device_present(dev->net) && + /* handle remote wakeup ASAP + * we cannot race against stop + */ + if (netif_device_present(dev->net) && !timer_pending(&dev->delay) && !test_bit(EVENT_RX_HALT, &dev->flags)) rx_alloc_submit(dev, GFP_NOIO); diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 2ec2041b62d4..c0e7c64765ab 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -285,7 +285,11 @@ static void veth_setup(struct net_device *dev) dev->ethtool_ops = &veth_ethtool_ops; dev->features |= NETIF_F_LLTX; dev->features |= VETH_FEATURES; - dev->vlan_features = dev->features; + dev->vlan_features = dev->features & + ~(NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_STAG_TX | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_STAG_RX); dev->destructor = veth_dev_free; dev->hw_features = VETH_FEATURES; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index d75f8edf4fb3..841b60831df1 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -671,8 +671,7 @@ static bool try_fill_recv(struct receive_queue *rq, gfp_t gfp) if (err) break; } while (rq->vq->num_free); - if (unlikely(!virtqueue_kick(rq->vq))) - return false; + virtqueue_kick(rq->vq); return !oom; } @@ -877,7 +876,7 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) err = xmit_skb(sq, skb); /* This should not happen! */ - if (unlikely(err) || unlikely(!virtqueue_kick(sq->vq))) { + if (unlikely(err)) { dev->stats.tx_fifo_errors++; if (net_ratelimit()) dev_warn(&dev->dev, @@ -886,6 +885,7 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) kfree_skb(skb); return NETDEV_TX_OK; } + virtqueue_kick(sq->vq); /* Don't wait up for transmitted skbs to be freed. */ skb_orphan(skb); @@ -1711,7 +1711,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)) diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 3be786faaaec..0fa3b44f7342 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -1762,11 +1762,20 @@ vmxnet3_netpoll(struct net_device *netdev) { struct vmxnet3_adapter *adapter = netdev_priv(netdev); - if (adapter->intr.mask_mode == VMXNET3_IMM_ACTIVE) - vmxnet3_disable_all_intrs(adapter); - - vmxnet3_do_poll(adapter, adapter->rx_queue[0].rx_ring[0].size); - vmxnet3_enable_all_intrs(adapter); + switch (adapter->intr.type) { +#ifdef CONFIG_PCI_MSI + case VMXNET3_IT_MSIX: { + int i; + for (i = 0; i < adapter->num_rx_queues; i++) + vmxnet3_msix_rx(0, &adapter->rx_queue[i]); + break; + } +#endif + case VMXNET3_IT_MSI: + default: + vmxnet3_intr(0, adapter->netdev); + break; + } } #endif /* CONFIG_NET_POLL_CONTROLLER */ diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index b0f705c2378f..1236812c7be6 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1318,6 +1318,9 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb) neigh_release(n); + if (reply == NULL) + goto out; + skb_reset_mac_header(reply); __skb_pull(reply, skb_network_offset(reply)); reply->ip_summed = CHECKSUM_UNNECESSARY; @@ -1339,15 +1342,103 @@ out: } #if IS_ENABLED(CONFIG_IPV6) + +static struct sk_buff *vxlan_na_create(struct sk_buff *request, + struct neighbour *n, bool isrouter) +{ + struct net_device *dev = request->dev; + struct sk_buff *reply; + struct nd_msg *ns, *na; + struct ipv6hdr *pip6; + u8 *daddr; + int na_olen = 8; /* opt hdr + ETH_ALEN for target */ + int ns_olen; + int i, len; + + if (dev == NULL) + return NULL; + + len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) + + sizeof(*na) + na_olen + dev->needed_tailroom; + reply = alloc_skb(len, GFP_ATOMIC); + if (reply == NULL) + return NULL; + + reply->protocol = htons(ETH_P_IPV6); + reply->dev = dev; + skb_reserve(reply, LL_RESERVED_SPACE(request->dev)); + skb_push(reply, sizeof(struct ethhdr)); + skb_set_mac_header(reply, 0); + + ns = (struct nd_msg *)skb_transport_header(request); + + daddr = eth_hdr(request)->h_source; + ns_olen = request->len - skb_transport_offset(request) - sizeof(*ns); + for (i = 0; i < ns_olen-1; i += (ns->opt[i+1]<<3)) { + if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) { + daddr = ns->opt + i + sizeof(struct nd_opt_hdr); + break; + } + } + + /* Ethernet header */ + ether_addr_copy(eth_hdr(reply)->h_dest, daddr); + ether_addr_copy(eth_hdr(reply)->h_source, n->ha); + eth_hdr(reply)->h_proto = htons(ETH_P_IPV6); + reply->protocol = htons(ETH_P_IPV6); + + skb_pull(reply, sizeof(struct ethhdr)); + skb_set_network_header(reply, 0); + skb_put(reply, sizeof(struct ipv6hdr)); + + /* IPv6 header */ + + pip6 = ipv6_hdr(reply); + memset(pip6, 0, sizeof(struct ipv6hdr)); + pip6->version = 6; + pip6->priority = ipv6_hdr(request)->priority; + pip6->nexthdr = IPPROTO_ICMPV6; + pip6->hop_limit = 255; + pip6->daddr = ipv6_hdr(request)->saddr; + pip6->saddr = *(struct in6_addr *)n->primary_key; + + skb_pull(reply, sizeof(struct ipv6hdr)); + skb_set_transport_header(reply, 0); + + na = (struct nd_msg *)skb_put(reply, sizeof(*na) + na_olen); + + /* Neighbor Advertisement */ + memset(na, 0, sizeof(*na)+na_olen); + na->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; + na->icmph.icmp6_router = isrouter; + na->icmph.icmp6_override = 1; + na->icmph.icmp6_solicited = 1; + na->target = ns->target; + ether_addr_copy(&na->opt[2], n->ha); + na->opt[0] = ND_OPT_TARGET_LL_ADDR; + na->opt[1] = na_olen >> 3; + + na->icmph.icmp6_cksum = csum_ipv6_magic(&pip6->saddr, + &pip6->daddr, sizeof(*na)+na_olen, IPPROTO_ICMPV6, + csum_partial(na, sizeof(*na)+na_olen, 0)); + + pip6->payload_len = htons(sizeof(*na)+na_olen); + + skb_push(reply, sizeof(struct ipv6hdr)); + + reply->ip_summed = CHECKSUM_UNNECESSARY; + + return reply; +} + static int neigh_reduce(struct net_device *dev, struct sk_buff *skb) { struct vxlan_dev *vxlan = netdev_priv(dev); - struct neighbour *n; - union vxlan_addr ipa; + struct nd_msg *msg; const struct ipv6hdr *iphdr; const struct in6_addr *saddr, *daddr; - struct nd_msg *msg; - struct inet6_dev *in6_dev = NULL; + struct neighbour *n; + struct inet6_dev *in6_dev; in6_dev = __in6_dev_get(dev); if (!in6_dev) @@ -1360,19 +1451,20 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb) saddr = &iphdr->saddr; daddr = &iphdr->daddr; - if (ipv6_addr_loopback(daddr) || - ipv6_addr_is_multicast(daddr)) - goto out; - msg = (struct nd_msg *)skb_transport_header(skb); if (msg->icmph.icmp6_code != 0 || msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION) goto out; - n = neigh_lookup(ipv6_stub->nd_tbl, daddr, dev); + if (ipv6_addr_loopback(daddr) || + ipv6_addr_is_multicast(&msg->target)) + goto out; + + n = neigh_lookup(ipv6_stub->nd_tbl, &msg->target, dev); if (n) { struct vxlan_fdb *f; + struct sk_buff *reply; if (!(n->nud_state & NUD_CONNECTED)) { neigh_release(n); @@ -1386,13 +1478,23 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb) goto out; } - ipv6_stub->ndisc_send_na(dev, n, saddr, &msg->target, - !!in6_dev->cnf.forwarding, - true, false, false); + reply = vxlan_na_create(skb, n, + !!(f ? f->flags & NTF_ROUTER : 0)); + neigh_release(n); + + if (reply == NULL) + goto out; + + if (netif_rx_ni(reply) == NET_RX_DROP) + dev->stats.rx_dropped++; + } else if (vxlan->flags & VXLAN_F_L3MISS) { - ipa.sin6.sin6_addr = *daddr; - ipa.sa.sa_family = AF_INET6; + union vxlan_addr ipa = { + .sin6.sin6_addr = msg->target, + .sa.sa_family = AF_INET6, + }; + vxlan_ip_miss(dev, &ipa); } diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index 1cc13569b17b..1b6b4d0cfa97 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -57,7 +57,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32365a5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, - {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e20, 0x000003a5, 0x000003a5, 0x000003a5, 0x000003a5}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282}, {0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27}, @@ -96,7 +96,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, - {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, + {0x0000ae20, 0x000001a6, 0x000001a6, 0x000001aa, 0x000001aa}, {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550}, }; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 11eab9f01fd8..9078a6c5a74e 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1534,7 +1534,7 @@ EXPORT_SYMBOL(ath9k_hw_check_nav); bool ath9k_hw_check_alive(struct ath_hw *ah) { int count = 50; - u32 reg; + u32 reg, last_val; if (AR_SREV_9300(ah)) return !ath9k_hw_detect_mac_hang(ah); @@ -1542,9 +1542,14 @@ bool ath9k_hw_check_alive(struct ath_hw *ah) if (AR_SREV_9285_12_OR_LATER(ah)) return true; + last_val = REG_READ(ah, AR_OBS_BUS_1); do { reg = REG_READ(ah, AR_OBS_BUS_1); + if (reg != last_val) + return true; + udelay(1); + last_val = reg; if ((reg & 0x7E7FFFEF) == 0x00702400) continue; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index a0ebdd000fc2..82e340d3ec60 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -732,11 +732,18 @@ static struct ath_rxbuf *ath_get_next_rx_buf(struct ath_softc *sc, return NULL; /* - * mark descriptor as zero-length and set the 'more' - * flag to ensure that both buffers get discarded + * Re-check previous descriptor, in case it has been filled + * in the mean time. */ - rs->rs_datalen = 0; - rs->rs_more = true; + ret = ath9k_hw_rxprocdesc(ah, ds, rs); + if (ret == -EINPROGRESS) { + /* + * mark descriptor as zero-length and set the 'more' + * flag to ensure that both buffers get discarded + */ + rs->rs_datalen = 0; + rs->rs_more = true; + } } list_del(&bf->list); @@ -985,32 +992,32 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_hdr *hdr; bool discard_current = sc->rx.discard_next; - int ret = 0; /* * Discard corrupt descriptors which are marked in * ath_get_next_rx_buf(). */ - sc->rx.discard_next = rx_stats->rs_more; if (discard_current) - return -EINVAL; + goto corrupt; + + sc->rx.discard_next = false; /* * Discard zero-length packets. */ if (!rx_stats->rs_datalen) { RX_STAT_INC(rx_len_err); - return -EINVAL; + goto corrupt; } - /* - * rs_status follows rs_datalen so if rs_datalen is too large - * we can take a hint that hardware corrupted it, so ignore - * those frames. - */ + /* + * rs_status follows rs_datalen so if rs_datalen is too large + * we can take a hint that hardware corrupted it, so ignore + * those frames. + */ if (rx_stats->rs_datalen > (common->rx_bufsize - ah->caps.rx_status_len)) { RX_STAT_INC(rx_len_err); - return -EINVAL; + goto corrupt; } /* Only use status info from the last fragment */ @@ -1024,10 +1031,8 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, * This is different from the other corrupt descriptor * condition handled above. */ - if (rx_stats->rs_status & ATH9K_RXERR_CORRUPT_DESC) { - ret = -EINVAL; - goto exit; - } + if (rx_stats->rs_status & ATH9K_RXERR_CORRUPT_DESC) + goto corrupt; hdr = (struct ieee80211_hdr *) (skb->data + ah->caps.rx_status_len); @@ -1043,18 +1048,15 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, if (ath_process_fft(sc, hdr, rx_stats, rx_status->mactime)) RX_STAT_INC(rx_spectral); - ret = -EINVAL; - goto exit; + return -EINVAL; } /* * everything but the rate is checked here, the rate check is done * separately to avoid doing two lookups for a rate for each frame. */ - if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error)) { - ret = -EINVAL; - goto exit; - } + if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error)) + return -EINVAL; if (ath_is_mybeacon(common, hdr)) { RX_STAT_INC(rx_beacons); @@ -1064,15 +1066,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, /* * This shouldn't happen, but have a safety check anyway. */ - if (WARN_ON(!ah->curchan)) { - ret = -EINVAL; - goto exit; - } + if (WARN_ON(!ah->curchan)) + return -EINVAL; - if (ath9k_process_rate(common, hw, rx_stats, rx_status)) { - ret =-EINVAL; - goto exit; - } + if (ath9k_process_rate(common, hw, rx_stats, rx_status)) + return -EINVAL; ath9k_process_rssi(common, hw, rx_stats, rx_status); @@ -1087,9 +1085,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, sc->rx.num_pkts++; #endif -exit: - sc->rx.discard_next = false; - return ret; + return 0; + +corrupt: + sc->rx.discard_next = rx_stats->rs_more; + return -EINVAL; } static void ath9k_rx_skb_postprocess(struct ath_common *common, diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 0a75e2f68c9d..55897d508a76 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1444,14 +1444,16 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, for (tidno = 0, tid = &an->tid[tidno]; tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { - if (!tid->sched) - continue; - ac = tid->ac; txq = ac->txq; ath_txq_lock(sc, txq); + if (!tid->sched) { + ath_txq_unlock(sc, txq); + continue; + } + buffered = ath_tid_has_buffered(tid); tid->sched = false; @@ -2061,7 +2063,7 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, ATH_TXBUF_RESET(bf); - if (tid) { + if (tid && ieee80211_is_data_present(hdr->frame_control)) { fragno = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; seqno = tid->seq_next; hdr->seq_ctrl = cpu_to_le16(tid->seq_next << IEEE80211_SEQ_SEQ_SHIFT); @@ -2184,14 +2186,15 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, txq->stopped = true; } + if (txctl->an && ieee80211_is_data_present(hdr->frame_control)) + tid = ath_get_skb_tid(sc, txctl->an, skb); + if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) { ath_txq_unlock(sc, txq); txq = sc->tx.uapsdq; ath_txq_lock(sc, txq); } else if (txctl->an && ieee80211_is_data_present(hdr->frame_control)) { - tid = ath_get_skb_tid(sc, txctl->an, skb); - WARN_ON(tid->ac->txq != txctl->txq); if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 3e991897d7ca..ddaa9efd053d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -457,7 +457,6 @@ struct brcmf_sdio { u8 tx_hdrlen; /* sdio bus header length for tx packet */ bool txglom; /* host tx glomming enable flag */ - struct sk_buff *txglom_sgpad; /* scatter-gather padding buffer */ u16 head_align; /* buffer pointer alignment */ u16 sgentry_align; /* scatter-gather buffer alignment */ }; @@ -1944,19 +1943,21 @@ static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, if (lastfrm && chain_pad) tail_pad += blksize - chain_pad; if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) { - pkt_pad = bus->txglom_sgpad; - if (pkt_pad == NULL) - brcmu_pkt_buf_get_skb(tail_pad + tail_chop); + pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop + + bus->head_align); if (pkt_pad == NULL) return -ENOMEM; ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + kfree_skb(pkt_pad); return ret; + } memcpy(pkt_pad->data, pkt->data + pkt->len - tail_chop, tail_chop); *(u32 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; skb_trim(pkt, pkt->len - tail_chop); + skb_trim(pkt_pad, tail_pad + tail_chop); __skb_queue_after(pktq, pkt, pkt_pad); } else { ntail = pkt->data_len + tail_pad - @@ -2011,7 +2012,7 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, return ret; head_pad = (u16)ret; if (head_pad) - memset(pkt_next->data, 0, head_pad + bus->tx_hdrlen); + memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad); total_len += pkt_next->len; @@ -3486,10 +3487,6 @@ static int brcmf_sdio_bus_preinit(struct device *dev) bus->txglom = false; value = 1; pad_size = bus->sdiodev->func[2]->cur_blksize << 1; - bus->txglom_sgpad = brcmu_pkt_buf_get_skb(pad_size); - if (!bus->txglom_sgpad) - brcmf_err("allocating txglom padding skb failed, reduced performance\n"); - err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom", &value, sizeof(u32)); if (err < 0) { @@ -4053,7 +4050,6 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) brcmf_sdio_chip_detach(&bus->ci); } - brcmu_pkt_buf_free_skb(bus->txglom_sgpad); kfree(bus->rxbuf); kfree(bus->hdrbuf); kfree(bus); diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index d36e252d2ccb..596525528f50 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -147,7 +147,7 @@ static void ap_free_sta(struct ap_data *ap, struct sta_info *sta) if (!sta->ap && sta->u.sta.challenge) kfree(sta->u.sta.challenge); - del_timer(&sta->timer); + del_timer_sync(&sta->timer); #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ kfree(sta); diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index c0d070c5df5e..9cdd91cdf661 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -590,6 +590,7 @@ void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, sizeof(priv->tid_data[sta_id][tid])); priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; priv->num_stations--; diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index a6839dfcb82d..398dd096674c 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -1291,8 +1291,6 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data; struct iwl_ht_agg *agg; struct sk_buff_head reclaimed_skbs; - struct ieee80211_tx_info *info; - struct ieee80211_hdr *hdr; struct sk_buff *skb; int sta_id; int tid; @@ -1379,22 +1377,28 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, freed = 0; skb_queue_walk(&reclaimed_skbs, skb) { - hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (ieee80211_is_data_qos(hdr->frame_control)) freed++; else WARN_ON_ONCE(1); - info = IEEE80211_SKB_CB(skb); iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); + memset(&info->status, 0, sizeof(info->status)); + /* Packet was transmitted successfully, failures come as single + * frames because before failing a frame the firmware transmits + * it without aggregation at least once. + */ + info->flags |= IEEE80211_TX_STAT_ACK; + if (freed == 1) { /* this is the first skb we deliver in this batch */ /* put the rate scaling data there */ info = IEEE80211_SKB_CB(skb); memset(&info->status, 0, sizeof(info->status)); - info->flags |= IEEE80211_TX_STAT_ACK; info->flags |= IEEE80211_TX_STAT_AMPDU; info->status.ampdu_ack_len = ba_resp->txed_2_done; info->status.ampdu_len = ba_resp->txed; diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c index 76cde6ce6551..18a895a949d4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c @@ -872,8 +872,11 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); - /* Rssi update while not associated ?! */ - if (WARN_ON_ONCE(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)) + /* + * Rssi update while not associated - can happen since the statistics + * are handled asynchronously + */ + if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) return; /* No BT - reports should be disabled */ diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index e4ead86f06d6..2b0ba1fc3c82 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -152,7 +152,7 @@ enum iwl_power_scheme { IWL_POWER_SCHEME_LP }; -#define IWL_CONN_MAX_LISTEN_INTERVAL 70 +#define IWL_CONN_MAX_LISTEN_INTERVAL 10 #define IWL_UAPSD_AC_INFO (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |\ IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |\ IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |\ diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 4df12fa9d336..76ee486039d7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -822,16 +822,12 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data; struct sk_buff_head reclaimed_skbs; struct iwl_mvm_tid_data *tid_data; - struct ieee80211_tx_info *info; struct ieee80211_sta *sta; struct iwl_mvm_sta *mvmsta; - struct ieee80211_hdr *hdr; struct sk_buff *skb; int sta_id, tid, freed; - /* "flow" corresponds to Tx queue */ u16 scd_flow = le16_to_cpu(ba_notif->scd_flow); - /* "ssn" is start of block-ack Tx window, corresponds to index * (in Tx queue's circular buffer) of first TFD/frame in window */ u16 ba_resp_scd_ssn = le16_to_cpu(ba_notif->scd_ssn); @@ -888,22 +884,26 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, freed = 0; skb_queue_walk(&reclaimed_skbs, skb) { - hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (ieee80211_is_data_qos(hdr->frame_control)) freed++; else WARN_ON_ONCE(1); - info = IEEE80211_SKB_CB(skb); iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); + memset(&info->status, 0, sizeof(info->status)); + /* Packet was transmitted successfully, failures come as single + * frames because before failing a frame the firmware transmits + * it without aggregation at least once. + */ + info->flags |= IEEE80211_TX_STAT_ACK; + if (freed == 1) { /* this is the first skb we deliver in this batch */ /* put the rate scaling data there */ - info = IEEE80211_SKB_CB(skb); - memset(&info->status, 0, sizeof(info->status)); - info->flags |= IEEE80211_TX_STAT_ACK; info->flags |= IEEE80211_TX_STAT_AMPDU; info->status.ampdu_ack_len = ba_notif->txed_2_done; info->status.ampdu_len = ba_notif->txed; diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index f47bcbe2945a..3872ead75488 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -359,13 +359,12 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { /* 7265 Series */ {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5112, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x510A, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)}, {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)}, diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 32f75007a825..cb6d189bc3e6 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -621,7 +621,7 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, id = *pos++; elen = *pos++; left -= 2; - if (elen > left || elen == 0) { + if (elen > left) { lbs_deb_scan("scan response: invalid IE fmt\n"); goto done; } diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c index 5e0eec4d71c7..5d9a8084665d 100644 --- a/drivers/net/wireless/mwifiex/11ac.c +++ b/drivers/net/wireless/mwifiex/11ac.c @@ -189,8 +189,7 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, vht_cap->header.len = cpu_to_le16(sizeof(struct ieee80211_vht_cap)); memcpy((u8 *)vht_cap + sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_vht_cap + - sizeof(struct ieee_types_header), + (u8 *)bss_desc->bcn_vht_cap, le16_to_cpu(vht_cap->header.len)); mwifiex_fill_vht_cap_tlv(priv, vht_cap, bss_desc->bss_band); diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 6261f8c53d44..7db1a89fdd95 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -308,8 +308,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, ht_cap->header.len = cpu_to_le16(sizeof(struct ieee80211_ht_cap)); memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header), - (u8 *) bss_desc->bcn_ht_cap + - sizeof(struct ieee_types_header), + (u8 *)bss_desc->bcn_ht_cap, le16_to_cpu(ht_cap->header.len)); mwifiex_fill_cap_info(priv, radio_type, ht_cap); diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 03688aa14e8a..7fe7b53fb17a 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -1211,6 +1211,12 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) rd_index = card->rxbd_rdptr & reg->rx_mask; skb_data = card->rx_buf_list[rd_index]; + /* If skb allocation was failed earlier for Rx packet, + * rx_buf_list[rd_index] would have been left with a NULL. + */ + if (!skb_data) + return -ENOMEM; + MWIFIEX_SKB_PACB(skb_data, &buf_pa); pci_unmap_single(card->dev, buf_pa, MWIFIEX_RX_DATA_BUF_SIZE, PCI_DMA_FROMDEVICE); @@ -1525,6 +1531,14 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) if (adapter->ps_state == PS_STATE_SLEEP_CFM) { mwifiex_process_sleep_confirm_resp(adapter, skb->data, skb->len); + mwifiex_pcie_enable_host_int(adapter); + if (mwifiex_write_reg(adapter, + PCIE_CPU_INT_EVENT, + CPU_INTR_SLEEP_CFM_DONE)) { + dev_warn(adapter->dev, + "Write register failed\n"); + return -1; + } while (reg->sleep_cookie && (count++ < 10) && mwifiex_pcie_ok_to_access_hw(adapter)) usleep_range(50, 60); @@ -1993,23 +2007,9 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) adapter->int_status |= pcie_ireg; spin_unlock_irqrestore(&adapter->int_lock, flags); - if (pcie_ireg & HOST_INTR_CMD_DONE) { - if ((adapter->ps_state == PS_STATE_SLEEP_CFM) || - (adapter->ps_state == PS_STATE_SLEEP)) { - mwifiex_pcie_enable_host_int(adapter); - if (mwifiex_write_reg(adapter, - PCIE_CPU_INT_EVENT, - CPU_INTR_SLEEP_CFM_DONE) - ) { - dev_warn(adapter->dev, - "Write register failed\n"); - return; - - } - } - } else if (!adapter->pps_uapsd_mode && - adapter->ps_state == PS_STATE_SLEEP && - mwifiex_pcie_ok_to_access_hw(adapter)) { + if (!adapter->pps_uapsd_mode && + adapter->ps_state == PS_STATE_SLEEP && + mwifiex_pcie_ok_to_access_hw(adapter)) { /* Potentially for PCIe we could get other * interrupts like shared. Don't change power * state until cookie is set */ diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 0a8a26e10f01..668547c2de84 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -2101,12 +2101,12 @@ mwifiex_save_curr_bcn(struct mwifiex_private *priv) curr_bss->ht_info_offset); if (curr_bss->bcn_vht_cap) - curr_bss->bcn_ht_cap = (void *)(curr_bss->beacon_buf + - curr_bss->vht_cap_offset); + curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf + + curr_bss->vht_cap_offset); if (curr_bss->bcn_vht_oper) - curr_bss->bcn_ht_oper = (void *)(curr_bss->beacon_buf + - curr_bss->vht_info_offset); + curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf + + curr_bss->vht_info_offset); if (curr_bss->bcn_bss_co_2040) curr_bss->bcn_bss_co_2040 = diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index e8ebbd4bc3cd..208748804a55 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -22,8 +22,6 @@ #define USB_VERSION "1.0" -static const char usbdriver_name[] = "usb8xxx"; - static struct mwifiex_if_ops usb_ops; static struct semaphore add_remove_card_sem; static struct usb_card_rec *usb_card; @@ -527,13 +525,6 @@ static int mwifiex_usb_resume(struct usb_interface *intf) MWIFIEX_BSS_ROLE_ANY), MWIFIEX_ASYNC_CMD); -#ifdef CONFIG_PM - /* Resume handler may be called due to remote wakeup, - * force to exit suspend anyway - */ - usb_disable_autosuspend(card->udev); -#endif /* CONFIG_PM */ - return 0; } @@ -567,13 +558,12 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf) } static struct usb_driver mwifiex_usb_driver = { - .name = usbdriver_name, + .name = "mwifiex_usb", .probe = mwifiex_usb_probe, .disconnect = mwifiex_usb_disconnect, .id_table = mwifiex_usb_table, .suspend = mwifiex_usb_suspend, .resume = mwifiex_usb_resume, - .supports_autosuspend = 1, }; static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 13eaeed03898..981cf6e7c73b 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -559,7 +559,8 @@ mwifiex_clean_txrx(struct mwifiex_private *priv) mwifiex_wmm_delete_all_ralist(priv); memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); - if (priv->adapter->if_ops.clean_pcie_ring) + if (priv->adapter->if_ops.clean_pcie_ring && + !priv->adapter->surprise_removed) priv->adapter->if_ops.clean_pcie_ring(priv->adapter); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); } diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 7f8b5d156c8c..41d4a8167dc3 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -5460,14 +5460,15 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 68, 0x0b); - rt2800_bbp_write(rt2x00dev, 69, 0x0d); - rt2800_bbp_write(rt2x00dev, 70, 0x06); + rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x13); rt2800_bbp_write(rt2x00dev, 75, 0x46); rt2800_bbp_write(rt2x00dev, 76, 0x28); rt2800_bbp_write(rt2x00dev, 77, 0x59); + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + rt2800_bbp_write(rt2x00dev, 79, 0x13); rt2800_bbp_write(rt2x00dev, 80, 0x05); rt2800_bbp_write(rt2x00dev, 81, 0x33); @@ -5510,7 +5511,6 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) if (rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_write(rt2x00dev, 134, 0xd0); rt2800_bbp_write(rt2x00dev, 135, 0xf6); - rt2800_bbp_write(rt2x00dev, 148, 0x84); } rt2800_disable_unused_dac_adc(rt2x00dev); diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index caddc1b427a9..42a2e06512f2 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -764,7 +764,7 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Overwrite TX done handler */ - PREPARE_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone); + INIT_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone); return 0; } diff --git a/drivers/net/wireless/ti/wl1251/rx.c b/drivers/net/wireless/ti/wl1251/rx.c index 123c4bb50e0a..cde0eaf99714 100644 --- a/drivers/net/wireless/ti/wl1251/rx.c +++ b/drivers/net/wireless/ti/wl1251/rx.c @@ -180,7 +180,7 @@ static void wl1251_rx_body(struct wl1251 *wl, wl1251_mem_read(wl, rx_packet_ring_addr, rx_buffer, length); /* The actual length doesn't include the target's alignment */ - skb->len = desc->length - PLCP_HEADER_LENGTH; + skb_trim(skb, desc->length - PLCP_HEADER_LENGTH); fc = (u16 *)skb->data; diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 7669d49a67e2..301cc037fda8 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -132,8 +132,7 @@ static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev) /* If the skb is GSO then we'll also need an extra slot for the * metadata. */ - if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4 || - skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) + if (skb_is_gso(skb)) min_slots_needed++; /* If the skb can't possibly fit in the remaining slots diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index e5284bca2d90..438d0c09b7e6 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -240,7 +240,7 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb, struct gnttab_copy *copy_gop; struct xenvif_rx_meta *meta; unsigned long bytes; - int gso_type; + int gso_type = XEN_NETIF_GSO_TYPE_NONE; /* Data must not cross a page boundary. */ BUG_ON(size + offset > PAGE_SIZE<<compound_order(page)); @@ -299,12 +299,12 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb, } /* Leave a gap for the GSO descriptor. */ - if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) - gso_type = XEN_NETIF_GSO_TYPE_TCPV4; - else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) - gso_type = XEN_NETIF_GSO_TYPE_TCPV6; - else - gso_type = XEN_NETIF_GSO_TYPE_NONE; + if (skb_is_gso(skb)) { + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) + gso_type = XEN_NETIF_GSO_TYPE_TCPV4; + else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) + gso_type = XEN_NETIF_GSO_TYPE_TCPV6; + } if (*head && ((1 << gso_type) & vif->gso_mask)) vif->rx.req_cons++; @@ -338,19 +338,15 @@ static int xenvif_gop_skb(struct sk_buff *skb, int head = 1; int old_meta_prod; int gso_type; - int gso_size; old_meta_prod = npo->meta_prod; - if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { - gso_type = XEN_NETIF_GSO_TYPE_TCPV4; - gso_size = skb_shinfo(skb)->gso_size; - } else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { - gso_type = XEN_NETIF_GSO_TYPE_TCPV6; - gso_size = skb_shinfo(skb)->gso_size; - } else { - gso_type = XEN_NETIF_GSO_TYPE_NONE; - gso_size = 0; + gso_type = XEN_NETIF_GSO_TYPE_NONE; + if (skb_is_gso(skb)) { + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) + gso_type = XEN_NETIF_GSO_TYPE_TCPV4; + else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) + gso_type = XEN_NETIF_GSO_TYPE_TCPV6; } /* Set up a GSO prefix descriptor, if necessary */ @@ -358,7 +354,7 @@ static int xenvif_gop_skb(struct sk_buff *skb, req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++); meta = npo->meta + npo->meta_prod++; meta->gso_type = gso_type; - meta->gso_size = gso_size; + meta->gso_size = skb_shinfo(skb)->gso_size; meta->size = 0; meta->id = req->id; } @@ -368,7 +364,7 @@ static int xenvif_gop_skb(struct sk_buff *skb, if ((1 << gso_type) & vif->gso_mask) { meta->gso_type = gso_type; - meta->gso_size = gso_size; + meta->gso_size = skb_shinfo(skb)->gso_size; } else { meta->gso_type = XEN_NETIF_GSO_TYPE_NONE; meta->gso_size = 0; @@ -500,8 +496,9 @@ static void xenvif_rx_action(struct xenvif *vif) size = skb_frag_size(&skb_shinfo(skb)->frags[i]); max_slots_needed += DIV_ROUND_UP(size, PAGE_SIZE); } - if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4 || - skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) + if (skb_is_gso(skb) && + (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4 || + skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)) max_slots_needed++; /* If the skb may not fit then bail out now */ diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index f9daa9e183f2..e30d80033cbc 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -907,6 +907,7 @@ static int handle_incoming_queue(struct net_device *dev, /* Ethernet work: Delayed to here as it peeks the header. */ skb->protocol = eth_type_trans(skb, dev); + skb_reset_network_header(skb); if (checksum_setup(dev, skb)) { kfree_skb(skb); diff --git a/drivers/of/base.c b/drivers/of/base.c index 89e888a78899..1b95a405628f 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -904,6 +904,38 @@ struct device_node *of_find_node_by_phandle(phandle handle) EXPORT_SYMBOL(of_find_node_by_phandle); /** + * of_property_count_elems_of_size - Count the number of elements in a property + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @elem_size: size of the individual element + * + * Search for a property in a device node and count the number of elements of + * size elem_size in it. Returns number of elements on sucess, -EINVAL if the + * property does not exist or its length does not match a multiple of elem_size + * and -ENODATA if the property does not have a value. + */ +int of_property_count_elems_of_size(const struct device_node *np, + const char *propname, int elem_size) +{ + struct property *prop = of_find_property(np, propname, NULL); + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + if (prop->length % elem_size != 0) { + pr_err("size of %s in node %s is not a multiple of %d\n", + propname, np->full_name, elem_size); + return -EINVAL; + } + + return prop->length / elem_size; +} +EXPORT_SYMBOL_GPL(of_property_count_elems_of_size); + +/** * of_find_property_value_of_size * * @np: device node from which the property value is to be read. diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 6a83ee1e9178..3fa66244ce32 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -905,7 +905,8 @@ int parport_claim_or_block(struct pardevice *dev) /* If dev->waiting is clear now, an interrupt gave us the port and we would deadlock if we slept. */ if (dev->waiting) { - interruptible_sleep_on (&dev->wait_q); + wait_event_interruptible(dev->wait_q, + !dev->waiting); if (signal_pending (current)) { return -EINTR; } diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 17d2b07ee67c..e2501ac6fe84 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -33,21 +33,15 @@ obj-$(CONFIG_PCI_IOV) += iov.o # # Some architectures use the generic PCI setup functions # -obj-$(CONFIG_X86) += setup-bus.o -obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o -obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o -obj-$(CONFIG_UNICORE32) += setup-bus.o setup-irq.o -obj-$(CONFIG_PARISC) += setup-bus.o -obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o -obj-$(CONFIG_PPC) += setup-bus.o -obj-$(CONFIG_FRV) += setup-bus.o -obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o +obj-$(CONFIG_ALPHA) += setup-irq.o +obj-$(CONFIG_ARM) += setup-irq.o +obj-$(CONFIG_UNICORE32) += setup-irq.o +obj-$(CONFIG_SUPERH) += setup-irq.o +obj-$(CONFIG_MIPS) += setup-irq.o obj-$(CONFIG_X86_VISWS) += setup-irq.o -obj-$(CONFIG_MN10300) += setup-bus.o -obj-$(CONFIG_MICROBLAZE) += setup-bus.o -obj-$(CONFIG_TILE) += setup-bus.o setup-irq.o -obj-$(CONFIG_SPARC_LEON) += setup-bus.o setup-irq.o -obj-$(CONFIG_M68K) += setup-bus.o setup-irq.o +obj-$(CONFIG_TILE) += setup-irq.o +obj-$(CONFIG_SPARC_LEON) += setup-irq.o +obj-$(CONFIG_M68K) += setup-irq.o # # ACPI Related PCI FW Functions diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 00660cc502c5..fb8aed307c28 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -132,7 +132,7 @@ static void pci_clip_resource_to_region(struct pci_bus *bus, static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, resource_size_t size, resource_size_t align, - resource_size_t min, unsigned int type_mask, + resource_size_t min, unsigned long type_mask, resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, @@ -144,7 +144,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, struct resource *r, avail; resource_size_t max; - type_mask |= IORESOURCE_IO | IORESOURCE_MEM; + type_mask |= IORESOURCE_TYPE_BITS; pci_bus_for_each_resource(bus, r, i) { if (!r) @@ -162,8 +162,6 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, avail = *r; pci_clip_resource_to_region(bus, &avail, region); - if (!resource_size(&avail)) - continue; /* * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to @@ -202,7 +200,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, */ int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, resource_size_t size, resource_size_t align, - resource_size_t min, unsigned int type_mask, + resource_size_t min, unsigned long type_mask, resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index 06ace6248c61..47aaf22d814e 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -32,11 +32,6 @@ void pci_set_host_bridge_release(struct pci_host_bridge *bridge, bridge->release_data = release_data; } -static bool resource_contains(struct resource *res1, struct resource *res2) -{ - return res1->start <= res2->start && res1->end >= res2->end; -} - void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, struct resource *res) { @@ -45,9 +40,6 @@ void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, resource_size_t offset = 0; list_for_each_entry(window, &bridge->windows, list) { - if (resource_type(res) != resource_type(window->res)) - continue; - if (resource_contains(window->res, res)) { offset = window->offset; break; diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 47d46c6d8468..a6f67ec8882f 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -27,7 +27,7 @@ config PCI_TEGRA config PCI_RCAR_GEN2 bool "Renesas R-Car Gen2 Internal PCI controller" - depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) + depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST) help Say Y here if you want internal PCI support on R-Car Gen2 SoC. There are 3 internal PCI controllers available with a single diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index e8663a8c3406..ee082509b0ba 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -424,20 +424,40 @@ static void imx6_pcie_reset_phy(struct pcie_port *pp) static int imx6_pcie_link_up(struct pcie_port *pp) { - u32 rc, ltssm, rx_valid; + u32 rc, debug_r0, rx_valid; + int count = 5; /* - * Test if the PHY reports that the link is up and also that - * the link training finished. It might happen that the PHY - * reports the link is already up, but the link training bit - * is still set, so make sure to check the training is done - * as well here. + * Test if the PHY reports that the link is up and also that the LTSSM + * training finished. There are three possible states of the link when + * this code is called: + * 1) The link is DOWN (unlikely) + * The link didn't come up yet for some reason. This usually means + * we have a real problem somewhere. Reset the PHY and exit. This + * state calls for inspection of the DEBUG registers. + * 2) The link is UP, but still in LTSSM training + * Wait for the training to finish, which should take a very short + * time. If the training does not finish, we have a problem and we + * need to inspect the DEBUG registers. If the training does finish, + * the link is up and operating correctly. + * 3) The link is UP and no longer in LTSSM training + * The link is up and operating correctly. */ - rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); - if ((rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP) && - !(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) - return 1; - + while (1) { + rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); + if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP)) + break; + if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) + return 1; + if (!count--) + break; + dev_dbg(pp->dev, "Link is up, but still in training\n"); + /* + * Wait a little bit, then re-check if the link finished + * the training. + */ + usleep_range(1000, 2000); + } /* * From L0, initiate MAC entry to gen2 if EP/RC supports gen2. * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2). @@ -446,15 +466,16 @@ static int imx6_pcie_link_up(struct pcie_port *pp) * to gen2 is stuck */ pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid); - ltssm = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F; + debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0); if (rx_valid & 0x01) return 0; - if (ltssm != 0x0d) + if ((debug_r0 & 0x3f) != 0x0d) return 0; dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n"); + dev_dbg(pp->dev, "debug_r0=%08x debug_r1=%08x\n", debug_r0, rc); imx6_pcie_reset_phy(pp); diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 0e79665afd44..d3d1cfd51e09 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -101,7 +101,9 @@ struct mvebu_pcie { struct mvebu_pcie_port *ports; struct msi_chip *msi; struct resource io; + char io_name[30]; struct resource realio; + char mem_name[30]; struct resource mem; struct resource busn; int nports; @@ -672,10 +674,30 @@ 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; - if (resource_size(&pcie->realio) != 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; + + if (request_resource(&iomem_resource, &pcie->mem)) + return 0; + + if (resource_size(&pcie->realio) != 0) { + if (request_resource(&ioport_resource, &pcie->realio)) { + release_resource(&pcie->mem); + return 0; + } pci_add_resource_offset(&sys->resources, &pcie->realio, sys->io_offset); + } pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); @@ -797,7 +819,7 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn, for (i = 0; i < nranges; i++) { u32 flags = of_read_number(range, 1); - u32 slot = of_read_number(range, 2); + u32 slot = of_read_number(range + 1, 1); u64 cpuaddr = of_read_number(range + na, pna); unsigned long rtype; diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index ceec147baec3..fd3e3ab56509 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -18,6 +18,7 @@ #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/sizes.h> #include <linux/slab.h> /* AHB-PCI Bridge PCI communication registers */ @@ -39,9 +40,26 @@ #define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20) #define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24) +#define RCAR_PCI_INT_SIGTABORT (1 << 0) +#define RCAR_PCI_INT_SIGRETABORT (1 << 1) +#define RCAR_PCI_INT_REMABORT (1 << 2) +#define RCAR_PCI_INT_PERR (1 << 3) +#define RCAR_PCI_INT_SIGSERR (1 << 4) +#define RCAR_PCI_INT_RESERR (1 << 5) +#define RCAR_PCI_INT_WIN1ERR (1 << 12) +#define RCAR_PCI_INT_WIN2ERR (1 << 13) #define RCAR_PCI_INT_A (1 << 16) #define RCAR_PCI_INT_B (1 << 17) #define RCAR_PCI_INT_PME (1 << 19) +#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \ + RCAR_PCI_INT_SIGRETABORT | \ + RCAR_PCI_INT_SIGRETABORT | \ + RCAR_PCI_INT_REMABORT | \ + RCAR_PCI_INT_PERR | \ + RCAR_PCI_INT_SIGSERR | \ + RCAR_PCI_INT_RESERR | \ + RCAR_PCI_INT_WIN1ERR | \ + RCAR_PCI_INT_WIN2ERR) #define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30) #define RCAR_AHB_BUS_MMODE_HTRANS (1 << 0) @@ -74,9 +92,6 @@ #define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48) -/* Number of internal PCI controllers */ -#define RCAR_PCI_NR_CONTROLLERS 3 - struct rcar_pci_priv { struct device *dev; void __iomem *reg; @@ -84,6 +99,7 @@ struct rcar_pci_priv { struct resource mem_res; struct resource *cfg_res; int irq; + unsigned long window_size; }; /* PCI configuration space operations */ @@ -102,6 +118,10 @@ static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, if (slot > 2) return NULL; + /* bridge logic only has registers to 0x40 */ + if (slot == 0x0 && where >= 0x40) + return NULL; + val = slot ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG : RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG; @@ -156,7 +176,7 @@ static int rcar_pci_write_config(struct pci_bus *bus, unsigned int devfn, } /* PCI interrupt mapping */ -static int __init rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct pci_sys_data *sys = dev->bus->sysdata; struct rcar_pci_priv *priv = sys->private_data; @@ -164,8 +184,48 @@ static int __init rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return priv->irq; } +#ifdef CONFIG_PCI_DEBUG +/* if debug enabled, then attach an error handler irq to the bridge */ + +static irqreturn_t rcar_pci_err_irq(int irq, void *pw) +{ + struct rcar_pci_priv *priv = pw; + u32 status = ioread32(priv->reg + RCAR_PCI_INT_STATUS_REG); + + if (status & RCAR_PCI_INT_ALLERRORS) { + dev_err(priv->dev, "error irq: status %08x\n", status); + + /* clear the error(s) */ + iowrite32(status & RCAR_PCI_INT_ALLERRORS, + priv->reg + RCAR_PCI_INT_STATUS_REG); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) +{ + int ret; + u32 val; + + ret = devm_request_irq(priv->dev, priv->irq, rcar_pci_err_irq, + IRQF_SHARED, "error irq", priv); + if (ret) { + dev_err(priv->dev, "cannot claim IRQ for error handling\n"); + return; + } + + val = ioread32(priv->reg + RCAR_PCI_INT_ENABLE_REG); + val |= RCAR_PCI_INT_ALLERRORS; + iowrite32(val, priv->reg + RCAR_PCI_INT_ENABLE_REG); +} +#else +static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { } +#endif + /* PCI host controller setup */ -static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) +static int rcar_pci_setup(int nr, struct pci_sys_data *sys) { struct rcar_pci_priv *priv = sys->private_data; void __iomem *reg = priv->reg; @@ -183,10 +243,31 @@ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) iowrite32(val, reg + RCAR_USBCTR_REG); udelay(4); - /* De-assert reset and set PCIAHB window1 size to 1GB */ + /* De-assert reset and reset PCIAHB window1 size */ val &= ~(RCAR_USBCTR_PCIAHB_WIN1_MASK | RCAR_USBCTR_PCICLK_MASK | RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST); - iowrite32(val | RCAR_USBCTR_PCIAHB_WIN1_1G, reg + RCAR_USBCTR_REG); + + /* Setup PCIAHB window1 size */ + switch (priv->window_size) { + case SZ_2G: + val |= RCAR_USBCTR_PCIAHB_WIN1_2G; + break; + case SZ_1G: + val |= RCAR_USBCTR_PCIAHB_WIN1_1G; + break; + case SZ_512M: + val |= RCAR_USBCTR_PCIAHB_WIN1_512M; + break; + default: + pr_warn("unknown window size %ld - defaulting to 256M\n", + priv->window_size); + priv->window_size = SZ_256M; + /* fall-through */ + case SZ_256M: + val |= RCAR_USBCTR_PCIAHB_WIN1_256M; + break; + } + iowrite32(val, reg + RCAR_USBCTR_REG); /* Configure AHB master and slave modes */ iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG); @@ -197,7 +278,7 @@ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) RCAR_PCI_ARBITER_PCIBP_MODE; iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG); - /* PCI-AHB mapping: 0x40000000-0x80000000 */ + /* PCI-AHB mapping: 0x40000000 base */ iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16, reg + RCAR_PCIAHB_WIN1_CTR_REG); @@ -224,10 +305,15 @@ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME, reg + RCAR_PCI_INT_ENABLE_REG); + if (priv->irq > 0) + rcar_pci_setup_errirq(priv); + /* Add PCI resources */ pci_add_resource(&sys->resources, &priv->io_res); pci_add_resource(&sys->resources, &priv->mem_res); + /* Setup bus number based on platform device id */ + sys->busnr = to_platform_device(priv->dev)->id; return 1; } @@ -236,48 +322,13 @@ static struct pci_ops rcar_pci_ops = { .write = rcar_pci_write_config, }; -static struct hw_pci rcar_hw_pci __initdata = { - .map_irq = rcar_pci_map_irq, - .ops = &rcar_pci_ops, - .setup = rcar_pci_setup, -}; - -static int rcar_pci_count __initdata; - -static int __init rcar_pci_add_controller(struct rcar_pci_priv *priv) -{ - void **private_data; - int count; - - if (rcar_hw_pci.nr_controllers < rcar_pci_count) - goto add_priv; - - /* (Re)allocate private data pointer array if needed */ - count = rcar_pci_count + RCAR_PCI_NR_CONTROLLERS; - private_data = kzalloc(count * sizeof(void *), GFP_KERNEL); - if (!private_data) - return -ENOMEM; - - rcar_pci_count = count; - if (rcar_hw_pci.private_data) { - memcpy(private_data, rcar_hw_pci.private_data, - rcar_hw_pci.nr_controllers * sizeof(void *)); - kfree(rcar_hw_pci.private_data); - } - - rcar_hw_pci.private_data = private_data; - -add_priv: - /* Add private data pointer to the array */ - rcar_hw_pci.private_data[rcar_hw_pci.nr_controllers++] = priv; - return 0; -} - -static int __init rcar_pci_probe(struct platform_device *pdev) +static int rcar_pci_probe(struct platform_device *pdev) { struct resource *cfg_res, *mem_res; struct rcar_pci_priv *priv; void __iomem *reg; + struct hw_pci hw; + void *hw_private[1]; cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg = devm_ioremap_resource(&pdev->dev, cfg_res); @@ -308,31 +359,34 @@ static int __init rcar_pci_probe(struct platform_device *pdev) priv->reg = reg; priv->dev = &pdev->dev; - return rcar_pci_add_controller(priv); + if (priv->irq < 0) { + dev_err(&pdev->dev, "no valid irq found\n"); + return priv->irq; + } + + priv->window_size = SZ_1G; + + hw_private[0] = priv; + memset(&hw, 0, sizeof(hw)); + hw.nr_controllers = ARRAY_SIZE(hw_private); + hw.private_data = hw_private; + hw.map_irq = rcar_pci_map_irq; + hw.ops = &rcar_pci_ops; + hw.setup = rcar_pci_setup; + pci_common_init_dev(&pdev->dev, &hw); + return 0; } static struct platform_driver rcar_pci_driver = { .driver = { .name = "pci-rcar-gen2", + .owner = THIS_MODULE, + .suppress_bind_attrs = true, }, + .probe = rcar_pci_probe, }; -static int __init rcar_pci_init(void) -{ - int retval; - - retval = platform_driver_probe(&rcar_pci_driver, rcar_pci_probe); - if (!retval) - pci_common_init(&rcar_hw_pci); - - /* Private data pointer array is not needed any more */ - kfree(rcar_hw_pci.private_data); - rcar_hw_pci.private_data = NULL; - - return retval; -} - -subsys_initcall(rcar_pci_init); +module_platform_driver(rcar_pci_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Renesas R-Car Gen2 internal PCI"); diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 17ce88f79d2b..509a29d84509 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -294,14 +294,12 @@ no_valid_irq: static void clear_irq(unsigned int irq) { unsigned int pos, nvec; - struct irq_desc *desc; struct msi_desc *msi; struct pcie_port *pp; struct irq_data *data = irq_get_irq_data(irq); /* get the port structure */ - desc = irq_to_desc(irq); - msi = irq_desc_get_msi_desc(desc); + msi = irq_data_get_msi(data); pp = sys_to_pcie(msi->dev->bus->sysdata); if (!pp) { BUG(); @@ -800,7 +798,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) /* setup RC BARs */ dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0); - dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_1); + dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1); /* setup interrupt pins */ dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE, &val); diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index b6162be4df40..2b859249303b 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -93,7 +93,6 @@ struct acpiphp_slot { struct list_head funcs; /* one slot may have different objects (i.e. for each function) */ struct slot *slot; - struct mutex crit_sect; u8 device; /* pci device# */ u32 flags; /* see below */ @@ -117,20 +116,30 @@ struct acpiphp_func { }; struct acpiphp_context { - acpi_handle handle; + struct acpi_hotplug_context hp; struct acpiphp_func func; struct acpiphp_bridge *bridge; unsigned int refcount; }; +static inline struct acpiphp_context *to_acpiphp_context(struct acpi_hotplug_context *hp) +{ + return container_of(hp, struct acpiphp_context, hp); +} + static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func) { return container_of(func, struct acpiphp_context, func); } +static inline struct acpi_device *func_to_acpi_device(struct acpiphp_func *func) +{ + return func_to_context(func)->hp.self; +} + static inline acpi_handle func_to_handle(struct acpiphp_func *func) { - return func_to_context(func)->handle; + return func_to_acpi_device(func)->handle; } /* @@ -158,7 +167,6 @@ struct acpiphp_attention_info #define FUNC_HAS_STA (0x00000001) #define FUNC_HAS_EJ0 (0x00000002) -#define FUNC_HAS_DCK (0x00000004) /* function prototypes */ diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 7c7a388c85ab..bccc27ee1030 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -58,71 +58,59 @@ static LIST_HEAD(bridge_list); static DEFINE_MUTEX(bridge_mutex); -static DEFINE_MUTEX(acpiphp_context_lock); -static void handle_hotplug_event(acpi_handle handle, u32 type, void *data); +static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type); +static void acpiphp_post_dock_fixup(struct acpi_device *adev); static void acpiphp_sanitize_bus(struct pci_bus *bus); static void acpiphp_set_hpp_values(struct pci_bus *bus); -static void hotplug_event(acpi_handle handle, u32 type, void *data); +static void hotplug_event(u32 type, struct acpiphp_context *context); static void free_bridge(struct kref *kref); -static void acpiphp_context_handler(acpi_handle handle, void *context) -{ - /* Intentionally empty. */ -} - /** * acpiphp_init_context - Create hotplug context and grab a reference to it. - * @handle: ACPI object handle to create the context for. + * @adev: ACPI device object to create the context for. * - * Call under acpiphp_context_lock. + * Call under acpi_hp_context_lock. */ -static struct acpiphp_context *acpiphp_init_context(acpi_handle handle) +static struct acpiphp_context *acpiphp_init_context(struct acpi_device *adev) { struct acpiphp_context *context; - acpi_status status; context = kzalloc(sizeof(*context), GFP_KERNEL); if (!context) return NULL; - context->handle = handle; context->refcount = 1; - status = acpi_attach_data(handle, acpiphp_context_handler, context); - if (ACPI_FAILURE(status)) { - kfree(context); - return NULL; - } + acpi_set_hp_context(adev, &context->hp, acpiphp_hotplug_notify, NULL, + acpiphp_post_dock_fixup); return context; } /** * acpiphp_get_context - Get hotplug context and grab a reference to it. - * @handle: ACPI object handle to get the context for. + * @adev: ACPI device object to get the context for. * - * Call under acpiphp_context_lock. + * Call under acpi_hp_context_lock. */ -static struct acpiphp_context *acpiphp_get_context(acpi_handle handle) +static struct acpiphp_context *acpiphp_get_context(struct acpi_device *adev) { - struct acpiphp_context *context = NULL; - acpi_status status; - void *data; + struct acpiphp_context *context; - status = acpi_get_data(handle, acpiphp_context_handler, &data); - if (ACPI_SUCCESS(status)) { - context = data; - context->refcount++; - } + if (!adev->hp) + return NULL; + + context = to_acpiphp_context(adev->hp); + context->refcount++; return context; } /** * acpiphp_put_context - Drop a reference to ACPI hotplug context. - * @handle: ACPI object handle to put the context for. + * @context: ACPI hotplug context to drop a reference to. * * The context object is removed if there are no more references to it. * - * Call under acpiphp_context_lock. + * Call under acpi_hp_context_lock. */ static void acpiphp_put_context(struct acpiphp_context *context) { @@ -130,7 +118,7 @@ static void acpiphp_put_context(struct acpiphp_context *context) return; WARN_ON(context->bridge); - acpi_detach_data(context->handle, acpiphp_context_handler); + context->hp.self->hp = NULL; kfree(context); } @@ -144,6 +132,27 @@ static inline void put_bridge(struct acpiphp_bridge *bridge) kref_put(&bridge->ref, free_bridge); } +static struct acpiphp_context *acpiphp_grab_context(struct acpi_device *adev) +{ + struct acpiphp_context *context; + + acpi_lock_hp_context(); + context = acpiphp_get_context(adev); + if (!context || context->func.parent->is_going_away) { + acpi_unlock_hp_context(); + return NULL; + } + get_bridge(context->func.parent); + acpiphp_put_context(context); + acpi_unlock_hp_context(); + return context; +} + +static void acpiphp_let_context_go(struct acpiphp_context *context) +{ + put_bridge(context->func.parent); +} + static void free_bridge(struct kref *kref) { struct acpiphp_context *context; @@ -151,7 +160,7 @@ static void free_bridge(struct kref *kref) struct acpiphp_slot *slot, *next; struct acpiphp_func *func, *tmp; - mutex_lock(&acpiphp_context_lock); + acpi_lock_hp_context(); bridge = container_of(kref, struct acpiphp_bridge, ref); @@ -175,31 +184,32 @@ static void free_bridge(struct kref *kref) pci_dev_put(bridge->pci_dev); kfree(bridge); - mutex_unlock(&acpiphp_context_lock); + acpi_unlock_hp_context(); } -/* - * the _DCK method can do funny things... and sometimes not - * hah-hah funny. +/** + * acpiphp_post_dock_fixup - Post-dock fixups for PCI devices. + * @adev: ACPI device object corresponding to a PCI device. * - * TBD - figure out a way to only call fixups for - * systems that require them. + * TBD - figure out a way to only call fixups for systems that require them. */ -static void post_dock_fixups(acpi_handle not_used, u32 event, void *data) +static void acpiphp_post_dock_fixup(struct acpi_device *adev) { - struct acpiphp_context *context = data; - struct pci_bus *bus = context->func.slot->bus; + struct acpiphp_context *context = acpiphp_grab_context(adev); + struct pci_bus *bus; u32 buses; - if (!bus->self) + if (!context) return; + bus = context->func.slot->bus; + if (!bus->self) + goto out; + /* fixup bad _DCK function that rewrites * secondary bridge on slot */ - pci_read_config_dword(bus->self, - PCI_PRIMARY_BUS, - &buses); + pci_read_config_dword(bus->self, PCI_PRIMARY_BUS, &buses); if (((buses >> 8) & 0xff) != bus->busn_res.start) { buses = (buses & 0xff000000) @@ -208,33 +218,11 @@ static void post_dock_fixups(acpi_handle not_used, u32 event, void *data) | ((unsigned int)(bus->busn_res.end) << 16); pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); } -} - -static void dock_event(acpi_handle handle, u32 type, void *data) -{ - struct acpiphp_context *context; - - mutex_lock(&acpiphp_context_lock); - context = acpiphp_get_context(handle); - if (!context || WARN_ON(context->handle != handle) - || context->func.parent->is_going_away) { - mutex_unlock(&acpiphp_context_lock); - return; - } - get_bridge(context->func.parent); - acpiphp_put_context(context); - mutex_unlock(&acpiphp_context_lock); - - hotplug_event(handle, type, data); - put_bridge(context->func.parent); + out: + acpiphp_let_context_go(context); } -static const struct acpi_dock_ops acpiphp_dock_ops = { - .fixup = post_dock_fixups, - .handler = dock_event, -}; - /* Check whether the PCI device is managed by native PCIe hotplug driver */ static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev) { @@ -264,26 +252,19 @@ static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev) return true; } -static void acpiphp_dock_init(void *data) -{ - struct acpiphp_context *context = data; - - get_bridge(context->func.parent); -} - -static void acpiphp_dock_release(void *data) -{ - struct acpiphp_context *context = data; - - put_bridge(context->func.parent); -} - -/* callback routine to register each ACPI PCI slot object */ -static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, - void **rv) +/** + * acpiphp_add_context - Add ACPIPHP context to an ACPI device object. + * @handle: ACPI handle of the object to add a context to. + * @lvl: Not used. + * @data: The object's parent ACPIPHP bridge. + * @rv: Not used. + */ +static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data, + void **rv) { struct acpiphp_bridge *bridge = data; struct acpiphp_context *context; + struct acpi_device *adev; struct acpiphp_slot *slot; struct acpiphp_func *newfunc; acpi_status status = AE_OK; @@ -293,9 +274,6 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, struct pci_dev *pdev = bridge->pci_dev; u32 val; - if (pdev && device_is_managed_by_native_pciehp(pdev)) - return AE_OK; - status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); if (ACPI_FAILURE(status)) { if (status != AE_NOT_FOUND) @@ -303,31 +281,34 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, "can't evaluate _ADR (%#x)\n", status); return AE_OK; } + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; device = (adr >> 16) & 0xffff; function = adr & 0xffff; - mutex_lock(&acpiphp_context_lock); - context = acpiphp_init_context(handle); + acpi_lock_hp_context(); + context = acpiphp_init_context(adev); if (!context) { - mutex_unlock(&acpiphp_context_lock); + acpi_unlock_hp_context(); acpi_handle_err(handle, "No hotplug context\n"); return AE_NOT_EXIST; } newfunc = &context->func; newfunc->function = function; newfunc->parent = bridge; - mutex_unlock(&acpiphp_context_lock); + acpi_unlock_hp_context(); - if (acpi_has_method(handle, "_EJ0")) + /* + * If this is a dock device, its _EJ0 should be executed by the dock + * notify handler after calling _DCK. + */ + if (!is_dock_device(adev) && acpi_has_method(handle, "_EJ0")) newfunc->flags = FUNC_HAS_EJ0; if (acpi_has_method(handle, "_STA")) newfunc->flags |= FUNC_HAS_STA; - if (acpi_has_method(handle, "_DCK")) - newfunc->flags |= FUNC_HAS_DCK; - /* search for objects that share the same slot */ list_for_each_entry(slot, &bridge->slots, node) if (slot->device == device) @@ -335,19 +316,26 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL); if (!slot) { - status = AE_NO_MEMORY; - goto err; + acpi_lock_hp_context(); + acpiphp_put_context(context); + acpi_unlock_hp_context(); + return AE_NO_MEMORY; } slot->bus = bridge->pci_bus; slot->device = device; INIT_LIST_HEAD(&slot->funcs); - mutex_init(&slot->crit_sect); list_add_tail(&slot->node, &bridge->slots); - /* Register slots for ejectable functions only. */ - if (acpi_pci_check_ejectable(pbus, handle) || is_dock_device(handle)) { + /* + * Expose slots to user space for functions that have _EJ0 or _RMV or + * are located in dock stations. Do not expose them for devices handled + * by the native PCIe hotplug (PCIeHP), becuase that code is supposed to + * expose slots to user space in those cases. + */ + if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(adev)) + && !(pdev && device_is_managed_by_native_pciehp(pdev))) { unsigned long long sun; int retval; @@ -381,44 +369,16 @@ static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data, &val, 60*1000)) slot->flags |= SLOT_ENABLED; - if (is_dock_device(handle)) { - /* we don't want to call this device's _EJ0 - * because we want the dock notify handler - * to call it after it calls _DCK - */ - newfunc->flags &= ~FUNC_HAS_EJ0; - if (register_hotplug_dock_device(handle, - &acpiphp_dock_ops, context, - acpiphp_dock_init, acpiphp_dock_release)) - pr_debug("failed to register dock device\n"); - } - - /* install notify handler */ - if (!(newfunc->flags & FUNC_HAS_DCK)) { - status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - handle_hotplug_event, - context); - if (ACPI_FAILURE(status)) - acpi_handle_err(handle, - "failed to install notify handler\n"); - } - return AE_OK; - - err: - mutex_lock(&acpiphp_context_lock); - acpiphp_put_context(context); - mutex_unlock(&acpiphp_context_lock); - return status; } -static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) +static struct acpiphp_bridge *acpiphp_dev_to_bridge(struct acpi_device *adev) { struct acpiphp_context *context; struct acpiphp_bridge *bridge = NULL; - mutex_lock(&acpiphp_context_lock); - context = acpiphp_get_context(handle); + acpi_lock_hp_context(); + context = acpiphp_get_context(adev); if (context) { bridge = context->bridge; if (bridge) @@ -426,7 +386,7 @@ static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle) acpiphp_put_context(context); } - mutex_unlock(&acpiphp_context_lock); + acpi_unlock_hp_context(); return bridge; } @@ -434,22 +394,15 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) { struct acpiphp_slot *slot; struct acpiphp_func *func; - acpi_status status; list_for_each_entry(slot, &bridge->slots, node) { list_for_each_entry(func, &slot->funcs, sibling) { - acpi_handle handle = func_to_handle(func); - - if (is_dock_device(handle)) - unregister_hotplug_dock_device(handle); + struct acpi_device *adev = func_to_acpi_device(func); - if (!(func->flags & FUNC_HAS_DCK)) { - status = acpi_remove_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - handle_hotplug_event); - if (ACPI_FAILURE(status)) - pr_err("failed to remove notify handler\n"); - } + acpi_lock_hp_context(); + adev->hp->notify = NULL; + adev->hp->fixup = NULL; + acpi_unlock_hp_context(); } slot->flags |= SLOT_IS_GOING_AWAY; if (slot->slot) @@ -460,9 +413,9 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) list_del(&bridge->list); mutex_unlock(&bridge_mutex); - mutex_lock(&acpiphp_context_lock); + acpi_lock_hp_context(); bridge->is_going_away = true; - mutex_unlock(&acpiphp_context_lock); + acpi_unlock_hp_context(); } /** @@ -471,7 +424,7 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) */ static unsigned char acpiphp_max_busnr(struct pci_bus *bus) { - struct list_head *tmp; + struct pci_bus *tmp; unsigned char max, n; /* @@ -484,41 +437,14 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus) */ max = bus->busn_res.start; - list_for_each(tmp, &bus->children) { - n = pci_bus_max_busnr(pci_bus_b(tmp)); + list_for_each_entry(tmp, &bus->children, node) { + n = pci_bus_max_busnr(tmp); if (n > max) max = n; } return max; } -/** - * acpiphp_bus_trim - Trim device objects in an ACPI namespace subtree. - * @handle: ACPI device object handle to start from. - */ -static void acpiphp_bus_trim(acpi_handle handle) -{ - struct acpi_device *adev = NULL; - - acpi_bus_get_device(handle, &adev); - if (adev) - acpi_bus_trim(adev); -} - -/** - * acpiphp_bus_add - Scan ACPI namespace subtree. - * @handle: ACPI object handle to start the scan from. - */ -static void acpiphp_bus_add(acpi_handle handle) -{ - struct acpi_device *adev = NULL; - - acpi_bus_scan(handle); - acpi_bus_get_device(handle, &adev); - if (acpi_device_enumerated(adev)) - acpi_device_set_power(adev, ACPI_STATE_D0); -} - static void acpiphp_set_acpi_region(struct acpiphp_slot *slot) { struct acpiphp_func *func; @@ -558,9 +484,13 @@ static int acpiphp_rescan_slot(struct acpiphp_slot *slot) { struct acpiphp_func *func; - list_for_each_entry(func, &slot->funcs, sibling) - acpiphp_bus_add(func_to_handle(func)); + list_for_each_entry(func, &slot->funcs, sibling) { + struct acpi_device *adev = func_to_acpi_device(func); + acpi_bus_scan(adev->handle); + if (acpi_device_enumerated(adev)) + acpi_device_set_power(adev, ACPI_STATE_D0); + } return pci_scan_slot(slot->bus, PCI_DEVFN(slot->device, 0)); } @@ -625,32 +555,15 @@ static void __ref enable_slot(struct acpiphp_slot *slot) } } -/* return first device in slot, acquiring a reference on it */ -static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot) -{ - struct pci_bus *bus = slot->bus; - struct pci_dev *dev; - struct pci_dev *ret = NULL; - - down_read(&pci_bus_sem); - list_for_each_entry(dev, &bus->devices, bus_list) - if (PCI_SLOT(dev->devfn) == slot->device) { - ret = pci_dev_get(dev); - break; - } - up_read(&pci_bus_sem); - - return ret; -} - /** * disable_slot - disable a slot * @slot: ACPI PHP slot */ static void disable_slot(struct acpiphp_slot *slot) { + struct pci_bus *bus = slot->bus; + struct pci_dev *dev, *prev; struct acpiphp_func *func; - struct pci_dev *pdev; /* * enable_slot() enumerates all functions in this device via @@ -658,22 +571,18 @@ static void disable_slot(struct acpiphp_slot *slot) * methods (_EJ0, etc.) or not. Therefore, we remove all functions * here. */ - while ((pdev = dev_in_slot(slot))) { - pci_stop_and_remove_bus_device(pdev); - pci_dev_put(pdev); - } + list_for_each_entry_safe_reverse(dev, prev, &bus->devices, bus_list) + if (PCI_SLOT(dev->devfn) == slot->device) + pci_stop_and_remove_bus_device(dev); list_for_each_entry(func, &slot->funcs, sibling) - acpiphp_bus_trim(func_to_handle(func)); + acpi_bus_trim(func_to_acpi_device(func)); slot->flags &= (~SLOT_ENABLED); } -static bool acpiphp_no_hotplug(acpi_handle handle) +static bool acpiphp_no_hotplug(struct acpi_device *adev) { - struct acpi_device *adev = NULL; - - acpi_bus_get_device(handle, &adev); return adev && adev->flags.no_hotplug; } @@ -682,7 +591,7 @@ static bool slot_no_hotplug(struct acpiphp_slot *slot) struct acpiphp_func *func; list_for_each_entry(func, &slot->funcs, sibling) - if (acpiphp_no_hotplug(func_to_handle(func))) + if (acpiphp_no_hotplug(func_to_acpi_device(func))) return true; return false; @@ -747,28 +656,25 @@ static inline bool device_status_valid(unsigned int sta) */ static void trim_stale_devices(struct pci_dev *dev) { - acpi_handle handle = ACPI_HANDLE(&dev->dev); + struct acpi_device *adev = ACPI_COMPANION(&dev->dev); struct pci_bus *bus = dev->subordinate; bool alive = false; - if (handle) { + if (adev) { acpi_status status; unsigned long long sta; - status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta); alive = (ACPI_SUCCESS(status) && device_status_valid(sta)) - || acpiphp_no_hotplug(handle); + || acpiphp_no_hotplug(adev); } - if (!alive) { - u32 v; + if (!alive) + alive = pci_device_is_present(dev); - /* Check if the device responds. */ - alive = pci_bus_read_dev_vendor_id(dev->bus, dev->devfn, &v, 0); - } if (!alive) { pci_stop_and_remove_bus_device(dev); - if (handle) - acpiphp_bus_trim(handle); + if (adev) + acpi_bus_trim(adev); } else if (bus) { struct pci_dev *child, *tmp; @@ -800,7 +706,6 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge) struct pci_bus *bus = slot->bus; struct pci_dev *dev, *tmp; - mutex_lock(&slot->crit_sect); if (slot_no_hotplug(slot)) { ; /* do nothing */ } else if (device_status_valid(get_slot_status(slot))) { @@ -815,7 +720,6 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge) } else { disable_slot(slot); } - mutex_unlock(&slot->crit_sect); } } @@ -855,11 +759,11 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus) * ACPI event handlers */ -void acpiphp_check_host_bridge(acpi_handle handle) +void acpiphp_check_host_bridge(struct acpi_device *adev) { struct acpiphp_bridge *bridge; - bridge = acpiphp_handle_to_bridge(handle); + bridge = acpiphp_dev_to_bridge(adev); if (bridge) { pci_lock_rescan_remove(); @@ -872,73 +776,52 @@ void acpiphp_check_host_bridge(acpi_handle handle) static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot); -static void hotplug_event(acpi_handle handle, u32 type, void *data) +static void hotplug_event(u32 type, struct acpiphp_context *context) { - struct acpiphp_context *context = data; + acpi_handle handle = context->hp.self->handle; struct acpiphp_func *func = &context->func; + struct acpiphp_slot *slot = func->slot; struct acpiphp_bridge *bridge; - char objname[64]; - struct acpi_buffer buffer = { .length = sizeof(objname), - .pointer = objname }; - mutex_lock(&acpiphp_context_lock); + acpi_lock_hp_context(); bridge = context->bridge; if (bridge) get_bridge(bridge); - mutex_unlock(&acpiphp_context_lock); + acpi_unlock_hp_context(); pci_lock_rescan_remove(); - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); switch (type) { case ACPI_NOTIFY_BUS_CHECK: /* bus re-enumerate */ - pr_debug("%s: Bus check notify on %s\n", __func__, objname); - pr_debug("%s: re-enumerating slots under %s\n", - __func__, objname); - if (bridge) { + acpi_handle_debug(handle, "Bus check in %s()\n", __func__); + if (bridge) acpiphp_check_bridge(bridge); - } else { - struct acpiphp_slot *slot = func->slot; - - if (slot->flags & SLOT_IS_GOING_AWAY) - break; - - mutex_lock(&slot->crit_sect); + else if (!(slot->flags & SLOT_IS_GOING_AWAY)) enable_slot(slot); - mutex_unlock(&slot->crit_sect); - } + break; case ACPI_NOTIFY_DEVICE_CHECK: /* device check */ - pr_debug("%s: Device check notify on %s\n", __func__, objname); + acpi_handle_debug(handle, "Device check in %s()\n", __func__); if (bridge) { acpiphp_check_bridge(bridge); - } else { - struct acpiphp_slot *slot = func->slot; - int ret; - - if (slot->flags & SLOT_IS_GOING_AWAY) - break; - + } else if (!(slot->flags & SLOT_IS_GOING_AWAY)) { /* * Check if anything has changed in the slot and rescan * from the parent if that's the case. */ - mutex_lock(&slot->crit_sect); - ret = acpiphp_rescan_slot(slot); - mutex_unlock(&slot->crit_sect); - if (ret) + if (acpiphp_rescan_slot(slot)) acpiphp_check_bridge(func->parent); } break; case ACPI_NOTIFY_EJECT_REQUEST: /* request device eject */ - pr_debug("%s: Device eject notify on %s\n", __func__, objname); - acpiphp_disable_and_eject_slot(func->slot); + acpi_handle_debug(handle, "Eject request in %s()\n", __func__); + acpiphp_disable_and_eject_slot(slot); break; } @@ -947,106 +830,41 @@ static void hotplug_event(acpi_handle handle, u32 type, void *data) put_bridge(bridge); } -static void hotplug_event_work(void *data, u32 type) +static int acpiphp_hotplug_notify(struct acpi_device *adev, u32 type) { - struct acpiphp_context *context = data; - acpi_handle handle = context->handle; - - acpi_scan_lock_acquire(); + struct acpiphp_context *context; - hotplug_event(handle, type, context); + context = acpiphp_grab_context(adev); + if (!context) + return -ENODATA; - acpi_scan_lock_release(); - acpi_evaluate_hotplug_ost(handle, type, ACPI_OST_SC_SUCCESS, NULL); - put_bridge(context->func.parent); + hotplug_event(type, context); + acpiphp_let_context_go(context); + return 0; } /** - * handle_hotplug_event - handle ACPI hotplug event - * @handle: Notify()'ed acpi_handle - * @type: Notify code - * @data: pointer to acpiphp_context structure + * acpiphp_enumerate_slots - Enumerate PCI slots for a given bus. + * @bus: PCI bus to enumerate the slots for. * - * Handles ACPI event notification on slots. - */ -static void handle_hotplug_event(acpi_handle handle, u32 type, void *data) -{ - struct acpiphp_context *context; - u32 ost_code = ACPI_OST_SC_SUCCESS; - acpi_status status; - - switch (type) { - case ACPI_NOTIFY_BUS_CHECK: - case ACPI_NOTIFY_DEVICE_CHECK: - break; - case ACPI_NOTIFY_EJECT_REQUEST: - ost_code = ACPI_OST_SC_EJECT_IN_PROGRESS; - acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); - break; - - case ACPI_NOTIFY_DEVICE_WAKE: - return; - - case ACPI_NOTIFY_FREQUENCY_MISMATCH: - acpi_handle_err(handle, "Device cannot be configured due " - "to a frequency mismatch\n"); - goto out; - - case ACPI_NOTIFY_BUS_MODE_MISMATCH: - acpi_handle_err(handle, "Device cannot be configured due " - "to a bus mode mismatch\n"); - goto out; - - case ACPI_NOTIFY_POWER_FAULT: - acpi_handle_err(handle, "Device has suffered a power fault\n"); - goto out; - - default: - acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type); - ost_code = ACPI_OST_SC_UNRECOGNIZED_NOTIFY; - goto out; - } - - mutex_lock(&acpiphp_context_lock); - context = acpiphp_get_context(handle); - if (!context || WARN_ON(context->handle != handle) - || context->func.parent->is_going_away) - goto err_out; - - get_bridge(context->func.parent); - acpiphp_put_context(context); - status = acpi_hotplug_execute(hotplug_event_work, context, type); - if (ACPI_SUCCESS(status)) { - mutex_unlock(&acpiphp_context_lock); - return; - } - put_bridge(context->func.parent); - - err_out: - mutex_unlock(&acpiphp_context_lock); - ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; - - out: - acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); -} - -/* - * Create hotplug slots for the PCI bus. - * It should always return 0 to avoid skipping following notifiers. + * A "slot" is an object associated with a PCI device number. All functions + * (PCI devices) with the same bus and device number belong to the same slot. */ void acpiphp_enumerate_slots(struct pci_bus *bus) { struct acpiphp_bridge *bridge; + struct acpi_device *adev; acpi_handle handle; acpi_status status; if (acpiphp_disabled) return; - handle = ACPI_HANDLE(bus->bridge); - if (!handle) + adev = ACPI_COMPANION(bus->bridge); + if (!adev) return; + handle = adev->handle; bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); if (!bridge) { acpi_handle_err(handle, "No memory for bridge object\n"); @@ -1074,10 +892,10 @@ void acpiphp_enumerate_slots(struct pci_bus *bus) * parent is going to be handled by pciehp, in which case this * bridge is not interesting to us either. */ - mutex_lock(&acpiphp_context_lock); - context = acpiphp_get_context(handle); + acpi_lock_hp_context(); + context = acpiphp_get_context(adev); if (!context) { - mutex_unlock(&acpiphp_context_lock); + acpi_unlock_hp_context(); put_device(&bus->dev); pci_dev_put(bridge->pci_dev); kfree(bridge); @@ -1087,17 +905,17 @@ void acpiphp_enumerate_slots(struct pci_bus *bus) context->bridge = bridge; /* Get a reference to the parent bridge. */ get_bridge(context->func.parent); - mutex_unlock(&acpiphp_context_lock); + acpi_unlock_hp_context(); } - /* must be added to the list prior to calling register_slot */ + /* Must be added to the list prior to calling acpiphp_add_context(). */ mutex_lock(&bridge_mutex); list_add(&bridge->list, &bridge_list); mutex_unlock(&bridge_mutex); /* register all slot objects under this bridge */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, - register_slot, NULL, bridge, NULL); + acpiphp_add_context, NULL, bridge, NULL); if (ACPI_FAILURE(status)) { acpi_handle_err(handle, "failed to register slots\n"); cleanup_bridge(bridge); @@ -1105,7 +923,10 @@ void acpiphp_enumerate_slots(struct pci_bus *bus) } } -/* Destroy hotplug slots associated with the PCI bus */ +/** + * acpiphp_remove_slots - Remove slot objects associated with a given bus. + * @bus: PCI bus to remove the slot objects for. + */ void acpiphp_remove_slots(struct pci_bus *bus) { struct acpiphp_bridge *bridge; @@ -1136,13 +957,10 @@ int acpiphp_enable_slot(struct acpiphp_slot *slot) if (slot->flags & SLOT_IS_GOING_AWAY) return -ENODEV; - mutex_lock(&slot->crit_sect); /* configure all functions */ if (!(slot->flags & SLOT_ENABLED)) enable_slot(slot); - mutex_unlock(&slot->crit_sect); - pci_unlock_rescan_remove(); return 0; } @@ -1158,8 +976,6 @@ static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) if (slot->flags & SLOT_IS_GOING_AWAY) return -ENODEV; - mutex_lock(&slot->crit_sect); - /* unconfigure all functions */ disable_slot(slot); @@ -1173,7 +989,6 @@ static int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot) break; } - mutex_unlock(&slot->crit_sect); return 0; } @@ -1181,9 +996,15 @@ int acpiphp_disable_slot(struct acpiphp_slot *slot) { int ret; + /* + * Acquire acpi_scan_lock to ensure that the execution of _EJ0 in + * acpiphp_disable_and_eject_slot() will be synchronized properly. + */ + acpi_scan_lock_acquire(); pci_lock_rescan_remove(); ret = acpiphp_disable_and_eject_slot(slot); pci_unlock_rescan_remove(); + acpi_scan_lock_release(); return ret; } diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 31273e155e6c..037e2612c5bd 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -920,12 +920,12 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bus->max_bus_speed = PCI_SPEED_100MHz_PCIX; break; } - if (bus_cap & 20) { + if (bus_cap & 0x20) { dbg("bus max supports 66MHz PCI-X\n"); bus->max_bus_speed = PCI_SPEED_66MHz_PCIX; break; } - if (bus_cap & 10) { + if (bus_cap & 0x10) { dbg("bus max supports 66MHz PCI\n"); bus->max_bus_speed = PCI_SPEED_66MHz; break; diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 88b37cad4b35..8a66866b8cf1 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -76,6 +76,7 @@ struct slot { struct hotplug_slot *hotplug_slot; struct delayed_work work; /* work for button event */ struct mutex lock; + struct mutex hotplug_lock; struct workqueue_struct *wq; }; @@ -109,6 +110,8 @@ struct controller { #define INT_BUTTON_PRESS 7 #define INT_BUTTON_RELEASE 8 #define INT_BUTTON_CANCEL 9 +#define INT_LINK_UP 10 +#define INT_LINK_DOWN 11 #define STATIC_STATE 0 #define BLINKINGON_STATE 1 @@ -132,6 +135,7 @@ u8 pciehp_handle_attention_button(struct slot *p_slot); u8 pciehp_handle_switch_change(struct slot *p_slot); u8 pciehp_handle_presence_change(struct slot *p_slot); u8 pciehp_handle_power_fault(struct slot *p_slot); +void pciehp_handle_linkstate_change(struct slot *p_slot); int pciehp_configure_device(struct slot *p_slot); int pciehp_unconfigure_device(struct slot *p_slot); void pciehp_queue_pushbutton_work(struct work_struct *work); @@ -153,6 +157,7 @@ void pciehp_green_led_on(struct slot *slot); void pciehp_green_led_off(struct slot *slot); void pciehp_green_led_blink(struct slot *slot); int pciehp_check_link_status(struct controller *ctrl); +bool pciehp_check_link_active(struct controller *ctrl); void pciehp_release_ctrl(struct controller *ctrl); int pciehp_reset_slot(struct slot *slot, int probe); diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index eddddd447d0d..20fea57d2149 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -112,6 +112,7 @@ static struct pcie_port_service_driver __initdata dummy_driver = { static int __init select_detection_mode(void) { struct dummy_slot *slot, *tmp; + if (pcie_port_service_register(&dummy_driver)) return PCIEHP_DETECT_ACPI; pcie_port_service_unregister(&dummy_driver); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 53b58debc288..0e0a2fff20a3 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -108,6 +108,7 @@ static int init_slot(struct controller *ctrl) ops = kzalloc(sizeof(*ops), GFP_KERNEL); if (!ops) goto out; + ops->enable_slot = enable_slot; ops->disable_slot = disable_slot; ops->get_power_status = get_power_status; @@ -283,8 +284,11 @@ static int pciehp_probe(struct pcie_device *dev) slot = ctrl->slot; pciehp_get_adapter_status(slot, &occupied); pciehp_get_power_status(slot, &poweron); - if (occupied && pciehp_force) + if (occupied && pciehp_force) { + mutex_lock(&slot->hotplug_lock); pciehp_enable_slot(slot); + mutex_unlock(&slot->hotplug_lock); + } /* If empty slot's power status is on, turn power off */ if (!occupied && poweron && POWER_CTRL(ctrl)) pciehp_power_off_slot(slot); @@ -328,10 +332,12 @@ static int pciehp_resume (struct pcie_device *dev) /* Check if slot is occupied */ pciehp_get_adapter_status(slot, &status); + mutex_lock(&slot->hotplug_lock); if (status) pciehp_enable_slot(slot); else pciehp_disable_slot(slot); + mutex_unlock(&slot->hotplug_lock); return 0; } #endif /* PM */ diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 50628487597d..c75e6a678dcc 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -150,6 +150,27 @@ u8 pciehp_handle_power_fault(struct slot *p_slot) return 1; } +void pciehp_handle_linkstate_change(struct slot *p_slot) +{ + u32 event_type; + struct controller *ctrl = p_slot->ctrl; + + /* Link Status Change */ + ctrl_dbg(ctrl, "Data Link Layer State change\n"); + + if (pciehp_check_link_active(ctrl)) { + ctrl_info(ctrl, "slot(%s): Link Up event\n", + slot_name(p_slot)); + event_type = INT_LINK_UP; + } else { + ctrl_info(ctrl, "slot(%s): Link Down event\n", + slot_name(p_slot)); + event_type = INT_LINK_DOWN; + } + + queue_interrupt_event(p_slot, event_type); +} + /* The following routines constitute the bulk of the hotplug controller logic */ @@ -212,7 +233,8 @@ static int board_added(struct slot *p_slot) if (retval) { ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", pci_domain_nr(parent), parent->number); - goto err_exit; + if (retval != -EEXIST) + goto err_exit; } pciehp_green_led_on(p_slot); @@ -255,6 +277,9 @@ static int remove_board(struct slot *p_slot) struct power_work_info { struct slot *p_slot; struct work_struct work; + unsigned int req; +#define DISABLE_REQ 0 +#define ENABLE_REQ 1 }; /** @@ -269,30 +294,38 @@ static void pciehp_power_thread(struct work_struct *work) struct power_work_info *info = container_of(work, struct power_work_info, work); struct slot *p_slot = info->p_slot; + int ret; - mutex_lock(&p_slot->lock); - switch (p_slot->state) { - case POWEROFF_STATE: - mutex_unlock(&p_slot->lock); + switch (info->req) { + case DISABLE_REQ: ctrl_dbg(p_slot->ctrl, "Disabling domain:bus:device=%04x:%02x:00\n", pci_domain_nr(p_slot->ctrl->pcie->port->subordinate), p_slot->ctrl->pcie->port->subordinate->number); + mutex_lock(&p_slot->hotplug_lock); pciehp_disable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; - break; - case POWERON_STATE: mutex_unlock(&p_slot->lock); - if (pciehp_enable_slot(p_slot)) + break; + case ENABLE_REQ: + ctrl_dbg(p_slot->ctrl, + "Enabling domain:bus:device=%04x:%02x:00\n", + pci_domain_nr(p_slot->ctrl->pcie->port->subordinate), + p_slot->ctrl->pcie->port->subordinate->number); + mutex_lock(&p_slot->hotplug_lock); + ret = pciehp_enable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); + if (ret) pciehp_green_led_off(p_slot); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; + mutex_unlock(&p_slot->lock); break; default: break; } - mutex_unlock(&p_slot->lock); kfree(info); } @@ -315,9 +348,11 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) switch (p_slot->state) { case BLINKINGOFF_STATE: p_slot->state = POWEROFF_STATE; + info->req = DISABLE_REQ; break; case BLINKINGON_STATE: p_slot->state = POWERON_STATE; + info->req = ENABLE_REQ; break; default: kfree(info); @@ -364,11 +399,10 @@ static void handle_button_press_event(struct slot *p_slot) */ ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot)); cancel_delayed_work(&p_slot->work); - if (p_slot->state == BLINKINGOFF_STATE) { + if (p_slot->state == BLINKINGOFF_STATE) pciehp_green_led_on(p_slot); - } else { + else pciehp_green_led_off(p_slot); - } pciehp_set_attention_status(p_slot, 0); ctrl_info(ctrl, "PCI slot #%s - action canceled " "due to button press\n", slot_name(p_slot)); @@ -407,14 +441,81 @@ static void handle_surprise_event(struct slot *p_slot) INIT_WORK(&info->work, pciehp_power_thread); pciehp_get_adapter_status(p_slot, &getstatus); - if (!getstatus) + if (!getstatus) { p_slot->state = POWEROFF_STATE; - else + info->req = DISABLE_REQ; + } else { p_slot->state = POWERON_STATE; + info->req = ENABLE_REQ; + } queue_work(p_slot->wq, &info->work); } +/* + * Note: This function must be called with slot->lock held + */ +static void handle_link_event(struct slot *p_slot, u32 event) +{ + struct controller *ctrl = p_slot->ctrl; + struct power_work_info *info; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n", + __func__); + return; + } + info->p_slot = p_slot; + info->req = event == INT_LINK_UP ? ENABLE_REQ : DISABLE_REQ; + INIT_WORK(&info->work, pciehp_power_thread); + + switch (p_slot->state) { + case BLINKINGON_STATE: + case BLINKINGOFF_STATE: + cancel_delayed_work(&p_slot->work); + /* Fall through */ + case STATIC_STATE: + p_slot->state = event == INT_LINK_UP ? + POWERON_STATE : POWEROFF_STATE; + queue_work(p_slot->wq, &info->work); + break; + case POWERON_STATE: + if (event == INT_LINK_UP) { + ctrl_info(ctrl, + "Link Up event ignored on slot(%s): already powering on\n", + slot_name(p_slot)); + kfree(info); + } else { + ctrl_info(ctrl, + "Link Down event queued on slot(%s): currently getting powered on\n", + slot_name(p_slot)); + p_slot->state = POWEROFF_STATE; + queue_work(p_slot->wq, &info->work); + } + break; + case POWEROFF_STATE: + if (event == INT_LINK_UP) { + ctrl_info(ctrl, + "Link Up event queued on slot(%s): currently getting powered off\n", + slot_name(p_slot)); + p_slot->state = POWERON_STATE; + queue_work(p_slot->wq, &info->work); + } else { + ctrl_info(ctrl, + "Link Down event ignored on slot(%s): already powering off\n", + slot_name(p_slot)); + kfree(info); + } + break; + default: + ctrl_err(ctrl, "Not a valid state on slot(%s)\n", + slot_name(p_slot)); + kfree(info); + break; + } +} + static void interrupt_event_handler(struct work_struct *work) { struct event_info *info = container_of(work, struct event_info, work); @@ -433,12 +534,23 @@ static void interrupt_event_handler(struct work_struct *work) pciehp_green_led_off(p_slot); break; case INT_PRESENCE_ON: - case INT_PRESENCE_OFF: if (!HP_SUPR_RM(ctrl)) break; + ctrl_dbg(ctrl, "Surprise Insertion\n"); + handle_surprise_event(p_slot); + break; + case INT_PRESENCE_OFF: + /* + * Regardless of surprise capability, we need to + * definitely remove a card that has been pulled out! + */ ctrl_dbg(ctrl, "Surprise Removal\n"); handle_surprise_event(p_slot); break; + case INT_LINK_UP: + case INT_LINK_DOWN: + handle_link_event(p_slot, info->event_type); + break; default: break; } @@ -447,6 +559,9 @@ static void interrupt_event_handler(struct work_struct *work) kfree(info); } +/* + * Note: This function must be called with slot->hotplug_lock held + */ int pciehp_enable_slot(struct slot *p_slot) { u8 getstatus = 0; @@ -479,13 +594,15 @@ int pciehp_enable_slot(struct slot *p_slot) pciehp_get_latch_status(p_slot, &getstatus); rc = board_added(p_slot); - if (rc) { + if (rc) pciehp_get_latch_status(p_slot, &getstatus); - } + return rc; } - +/* + * Note: This function must be called with slot->hotplug_lock held + */ int pciehp_disable_slot(struct slot *p_slot) { u8 getstatus = 0; @@ -494,24 +611,6 @@ int pciehp_disable_slot(struct slot *p_slot) if (!p_slot->ctrl) return 1; - if (!HP_SUPR_RM(p_slot->ctrl)) { - pciehp_get_adapter_status(p_slot, &getstatus); - if (!getstatus) { - ctrl_info(ctrl, "No adapter on slot(%s)\n", - slot_name(p_slot)); - return -ENODEV; - } - } - - if (MRL_SENS(p_slot->ctrl)) { - pciehp_get_latch_status(p_slot, &getstatus); - if (getstatus) { - ctrl_info(ctrl, "Latch open on slot(%s)\n", - slot_name(p_slot)); - return -ENODEV; - } - } - if (POWER_CTRL(p_slot->ctrl)) { pciehp_get_power_status(p_slot, &getstatus); if (!getstatus) { @@ -536,7 +635,9 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot) case STATIC_STATE: p_slot->state = POWERON_STATE; mutex_unlock(&p_slot->lock); + mutex_lock(&p_slot->hotplug_lock); retval = pciehp_enable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; break; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 14acfccb7670..d7d058fa19a4 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -206,7 +206,7 @@ static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) mutex_unlock(&ctrl->ctrl_lock); } -static bool check_link_active(struct controller *ctrl) +bool pciehp_check_link_active(struct controller *ctrl) { struct pci_dev *pdev = ctrl_dev(ctrl); u16 lnk_status; @@ -225,12 +225,12 @@ static void __pcie_wait_link_active(struct controller *ctrl, bool active) { int timeout = 1000; - if (check_link_active(ctrl) == active) + if (pciehp_check_link_active(ctrl) == active) return; while (timeout > 0) { msleep(10); timeout -= 10; - if (check_link_active(ctrl) == active) + if (pciehp_check_link_active(ctrl) == active) return; } ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n", @@ -242,11 +242,6 @@ static void pcie_wait_link_active(struct controller *ctrl) __pcie_wait_link_active(ctrl, true); } -static void pcie_wait_link_not_active(struct controller *ctrl) -{ - __pcie_wait_link_active(ctrl, false); -} - static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) { u32 l; @@ -332,11 +327,6 @@ static int pciehp_link_enable(struct controller *ctrl) return __pciehp_link_set(ctrl, true); } -static int pciehp_link_disable(struct controller *ctrl) -{ - return __pciehp_link_set(ctrl, false); -} - void pciehp_get_attention_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; @@ -508,14 +498,6 @@ void pciehp_power_off_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; - /* Disable the link at first */ - pciehp_link_disable(ctrl); - /* wait the link is down */ - if (ctrl->link_active_reporting) - pcie_wait_link_not_active(ctrl); - else - msleep(1000); - pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, @@ -540,7 +522,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | - PCI_EXP_SLTSTA_CC); + PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC); detected &= ~intr_loc; intr_loc |= detected; if (!intr_loc) @@ -579,6 +561,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) ctrl->power_fault_detected = 1; pciehp_handle_power_fault(slot); } + + if (intr_loc & PCI_EXP_SLTSTA_DLLSC) + pciehp_handle_linkstate_change(slot); + return IRQ_HANDLED; } @@ -596,9 +582,17 @@ void pcie_enable_notification(struct controller *ctrl) * when it is cleared in the interrupt service routine, and * next power fault detected interrupt was notified again. */ - cmd = PCI_EXP_SLTCTL_PDCE; + + /* + * Always enable link events: thus link-up and link-down shall + * always be treated as hotplug and unplug respectively. Enable + * presence detect only if Attention Button is not present. + */ + cmd = PCI_EXP_SLTCTL_DLLSCE; if (ATTN_BUTTN(ctrl)) cmd |= PCI_EXP_SLTCTL_ABPE; + else + cmd |= PCI_EXP_SLTCTL_PDCE; if (MRL_SENS(ctrl)) cmd |= PCI_EXP_SLTCTL_MRLSCE; if (!pciehp_poll_mode) @@ -606,7 +600,8 @@ void pcie_enable_notification(struct controller *ctrl) mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | - PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE); + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_DLLSCE); pcie_write_cmd(ctrl, cmd, mask); } @@ -624,33 +619,38 @@ static void pcie_disable_notification(struct controller *ctrl) /* * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary - * bus reset of the bridge, but if the slot supports surprise removal we need - * to disable presence detection around the bus reset and clear any spurious + * bus reset of the bridge, but at the same time we want to ensure that it is + * not seen as a hot-unplug, followed by the hot-plug of the device. Thus, + * disable link state notification and presence detection change notification + * momentarily, if we see that they could interfere. Also, clear any spurious * events after. */ int pciehp_reset_slot(struct slot *slot, int probe) { struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); + u16 stat_mask = 0, ctrl_mask = 0; if (probe) return 0; - if (HP_SUPR_RM(ctrl)) { - pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_PDCE); - if (pciehp_poll_mode) - del_timer_sync(&ctrl->poll_timer); + if (!ATTN_BUTTN(ctrl)) { + ctrl_mask |= PCI_EXP_SLTCTL_PDCE; + stat_mask |= PCI_EXP_SLTSTA_PDC; } + ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE; + stat_mask |= PCI_EXP_SLTSTA_DLLSC; + + pcie_write_cmd(ctrl, 0, ctrl_mask); + if (pciehp_poll_mode) + del_timer_sync(&ctrl->poll_timer); pci_reset_bridge_secondary_bus(ctrl->pcie->port); - if (HP_SUPR_RM(ctrl)) { - pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDC); - pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE); - if (pciehp_poll_mode) - int_poll_timeout(ctrl->poll_timer.data); - } + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask); + pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask); + if (pciehp_poll_mode) + int_poll_timeout(ctrl->poll_timer.data); return 0; } @@ -687,6 +687,7 @@ static int pcie_init_slot(struct controller *ctrl) slot->ctrl = ctrl; mutex_init(&slot->lock); + mutex_init(&slot->hotplug_lock); INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); ctrl->slot = slot; return 0; diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index b07d7cc2d697..1b533060ce65 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -50,7 +50,7 @@ int pciehp_configure_device(struct slot *p_slot) "at %04x:%02x:00, cannot hot-add\n", pci_name(dev), pci_domain_nr(parent), parent->number); pci_dev_put(dev); - ret = -EINVAL; + ret = -EEXIST; goto out; } diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 9dce7c5e2a77..de7a74782f92 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -170,97 +170,6 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) pci_dev_put(dev); } -static int sriov_migration(struct pci_dev *dev) -{ - u16 status; - struct pci_sriov *iov = dev->sriov; - - if (!iov->num_VFs) - return 0; - - if (!(iov->cap & PCI_SRIOV_CAP_VFM)) - return 0; - - pci_read_config_word(dev, iov->pos + PCI_SRIOV_STATUS, &status); - if (!(status & PCI_SRIOV_STATUS_VFM)) - return 0; - - schedule_work(&iov->mtask); - - return 1; -} - -static void sriov_migration_task(struct work_struct *work) -{ - int i; - u8 state; - u16 status; - struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask); - - for (i = iov->initial_VFs; i < iov->num_VFs; i++) { - state = readb(iov->mstate + i); - if (state == PCI_SRIOV_VFM_MI) { - writeb(PCI_SRIOV_VFM_AV, iov->mstate + i); - state = readb(iov->mstate + i); - if (state == PCI_SRIOV_VFM_AV) - virtfn_add(iov->self, i, 1); - } else if (state == PCI_SRIOV_VFM_MO) { - virtfn_remove(iov->self, i, 1); - writeb(PCI_SRIOV_VFM_UA, iov->mstate + i); - state = readb(iov->mstate + i); - if (state == PCI_SRIOV_VFM_AV) - virtfn_add(iov->self, i, 0); - } - } - - pci_read_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, &status); - status &= ~PCI_SRIOV_STATUS_VFM; - pci_write_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, status); -} - -static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn) -{ - int bir; - u32 table; - resource_size_t pa; - struct pci_sriov *iov = dev->sriov; - - if (nr_virtfn <= iov->initial_VFs) - return 0; - - pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table); - bir = PCI_SRIOV_VFM_BIR(table); - if (bir > PCI_STD_RESOURCE_END) - return -EIO; - - table = PCI_SRIOV_VFM_OFFSET(table); - if (table + nr_virtfn > pci_resource_len(dev, bir)) - return -EIO; - - pa = pci_resource_start(dev, bir) + table; - iov->mstate = ioremap(pa, nr_virtfn); - if (!iov->mstate) - return -ENOMEM; - - INIT_WORK(&iov->mtask, sriov_migration_task); - - iov->ctrl |= PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR; - pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); - - return 0; -} - -static void sriov_disable_migration(struct pci_dev *dev) -{ - struct pci_sriov *iov = dev->sriov; - - iov->ctrl &= ~(PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR); - pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); - - cancel_work_sync(&iov->mtask); - iounmap(iov->mstate); -} - static int sriov_enable(struct pci_dev *dev, int nr_virtfn) { int rc; @@ -351,12 +260,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) goto failed; } - if (iov->cap & PCI_SRIOV_CAP_VFM) { - rc = sriov_enable_migration(dev, nr_virtfn); - if (rc) - goto failed; - } - kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); iov->num_VFs = nr_virtfn; @@ -387,9 +290,6 @@ static void sriov_disable(struct pci_dev *dev) if (!iov->num_VFs) return; - if (iov->cap & PCI_SRIOV_CAP_VFM) - sriov_disable_migration(dev); - for (i = 0; i < iov->num_VFs; i++) virtfn_remove(dev, i, 0); @@ -688,25 +588,6 @@ void pci_disable_sriov(struct pci_dev *dev) EXPORT_SYMBOL_GPL(pci_disable_sriov); /** - * pci_sriov_migration - notify SR-IOV core of Virtual Function Migration - * @dev: the PCI device - * - * Returns IRQ_HANDLED if the IRQ is handled, or IRQ_NONE if not. - * - * Physical Function driver is responsible to register IRQ handler using - * VF Migration Interrupt Message Number, and call this function when the - * interrupt is generated by the hardware. - */ -irqreturn_t pci_sriov_migration(struct pci_dev *dev) -{ - if (!dev->is_physfn) - return IRQ_NONE; - - return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; -} -EXPORT_SYMBOL_GPL(pci_sriov_migration); - -/** * pci_num_vf - return number of VFs associated with a PF device_release_driver * @dev: the PCI device * diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 25f0bc659164..d911e0c1f359 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -616,15 +616,11 @@ static int pci_pm_prepare(struct device *dev) int error = 0; /* - * PCI devices suspended at run time need to be resumed at this - * point, because in general it is necessary to reconfigure them for - * system suspend. Namely, if the device is supposed to wake up the - * system from the sleep state, we may need to reconfigure it for this - * purpose. In turn, if the device is not supposed to wake up the - * system from the sleep state, we'll have to prevent it from signaling - * wake-up. + * Devices having power.ignore_children set may still be necessary for + * suspending their children in the next phase of device suspend. */ - pm_runtime_resume(dev); + if (dev->power.ignore_children) + pm_runtime_resume(dev); if (drv && drv->pm && drv->pm->prepare) error = drv->pm->prepare(dev); @@ -654,6 +650,16 @@ static int pci_pm_suspend(struct device *dev) goto Fixup; } + /* + * PCI devices suspended at run time need to be resumed at this point, + * because in general it is necessary to reconfigure them for system + * suspend. Namely, if the device is supposed to wake up the system + * from the sleep state, we may need to reconfigure it for this purpose. + * In turn, if the device is not supposed to wake up the system from the + * sleep state, we'll have to prevent it from signaling wake-up. + */ + pm_runtime_resume(dev); + pci_dev->state_saved = false; if (pm->suspend) { pci_power_t prev = pci_dev->current_state; @@ -808,6 +814,14 @@ static int pci_pm_freeze(struct device *dev) return 0; } + /* + * This used to be done in pci_pm_prepare() for all devices and some + * drivers may depend on it, so do it here. Ideally, runtime-suspended + * devices should not be touched during freeze/thaw transitions, + * however. + */ + pm_runtime_resume(dev); + pci_dev->state_saved = false; if (pm->freeze) { int error; @@ -915,6 +929,9 @@ static int pci_pm_poweroff(struct device *dev) goto Fixup; } + /* The reason to do that is the same as in pci_pm_suspend(). */ + pm_runtime_resume(dev); + pci_dev->state_saved = false; if (pm->poweroff) { int error; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6b05f6134b68..7325d43bf030 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -108,12 +108,12 @@ static bool pcie_ari_disabled; */ unsigned char pci_bus_max_busnr(struct pci_bus* bus) { - struct list_head *tmp; + struct pci_bus *tmp; unsigned char max, n; max = bus->busn_res.end; - list_for_each(tmp, &bus->children) { - n = pci_bus_max_busnr(pci_bus_b(tmp)); + list_for_each_entry(tmp, &bus->children, node) { + n = pci_bus_max_busnr(tmp); if(n > max) max = n; } @@ -401,33 +401,40 @@ EXPORT_SYMBOL_GPL(pci_find_ht_capability); * @res: child resource record for which parent is sought * * For given resource region of given device, return the resource - * region of parent bus the given region is contained in or where - * it should be allocated from. + * region of parent bus the given region is contained in. */ struct resource * pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) { const struct pci_bus *bus = dev->bus; + struct resource *r; int i; - struct resource *best = NULL, *r; pci_bus_for_each_resource(bus, r, i) { if (!r) continue; - if (res->start && !(res->start >= r->start && res->end <= r->end)) - continue; /* Not contained */ - if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM)) - continue; /* Wrong type */ - if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH)) - return r; /* Exact match */ - /* We can't insert a non-prefetch resource inside a prefetchable parent .. */ - if (r->flags & IORESOURCE_PREFETCH) - continue; - /* .. but we can put a prefetchable resource inside a non-prefetchable one */ - if (!best) - best = r; + if (res->start && resource_contains(r, res)) { + + /* + * If the window is prefetchable but the BAR is + * not, the allocator made a mistake. + */ + if (r->flags & IORESOURCE_PREFETCH && + !(res->flags & IORESOURCE_PREFETCH)) + return NULL; + + /* + * If we're below a transparent bridge, there may + * be both a positively-decoded aperture and a + * subtractively-decoded region that contain the BAR. + * We want the positively-decoded one, so this depends + * on pci_bus_for_each_resource() giving us those + * first. + */ + return r; + } } - return best; + return NULL; } /** @@ -1178,6 +1185,11 @@ int pci_load_and_free_saved_state(struct pci_dev *dev, } EXPORT_SYMBOL_GPL(pci_load_and_free_saved_state); +int __weak pcibios_enable_device(struct pci_dev *dev, int bars) +{ + return pci_enable_resources(dev, bars); +} + static int do_pci_enable_device(struct pci_dev *dev, int bars) { int err; @@ -1192,6 +1204,9 @@ static int do_pci_enable_device(struct pci_dev *dev, int bars) return err; pci_fixup_device(pci_fixup_enable, dev); + if (dev->msi_enabled || dev->msix_enabled) + return 0; + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); if (pin) { pci_read_config_word(dev, PCI_COMMAND, &cmd); @@ -1621,29 +1636,27 @@ static void pci_pme_list_scan(struct work_struct *work) struct pci_pme_device *pme_dev, *n; mutex_lock(&pci_pme_list_mutex); - if (!list_empty(&pci_pme_list)) { - list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { - if (pme_dev->dev->pme_poll) { - struct pci_dev *bridge; - - bridge = pme_dev->dev->bus->self; - /* - * If bridge is in low power state, the - * configuration space of subordinate devices - * may be not accessible - */ - if (bridge && bridge->current_state != PCI_D0) - continue; - pci_pme_wakeup(pme_dev->dev, NULL); - } else { - list_del(&pme_dev->list); - kfree(pme_dev); - } + list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { + if (pme_dev->dev->pme_poll) { + struct pci_dev *bridge; + + bridge = pme_dev->dev->bus->self; + /* + * If bridge is in low power state, the + * configuration space of subordinate devices + * may be not accessible + */ + if (bridge && bridge->current_state != PCI_D0) + continue; + pci_pme_wakeup(pme_dev->dev, NULL); + } else { + list_del(&pme_dev->list); + kfree(pme_dev); } - if (!list_empty(&pci_pme_list)) - schedule_delayed_work(&pci_pme_work, - msecs_to_jiffies(PME_TIMEOUT)); } + if (!list_empty(&pci_pme_list)) + schedule_delayed_work(&pci_pme_work, + msecs_to_jiffies(PME_TIMEOUT)); mutex_unlock(&pci_pme_list_mutex); } @@ -2190,21 +2203,18 @@ void pci_request_acs(void) } /** - * pci_enable_acs - enable ACS if hardware support it + * pci_std_enable_acs - enable ACS on devices using standard ACS capabilites * @dev: the PCI device */ -void pci_enable_acs(struct pci_dev *dev) +static int pci_std_enable_acs(struct pci_dev *dev) { int pos; u16 cap; u16 ctrl; - if (!pci_acs_enable) - return; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); if (!pos) - return; + return -ENODEV; pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap); pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); @@ -2222,6 +2232,23 @@ void pci_enable_acs(struct pci_dev *dev) ctrl |= (cap & PCI_ACS_UF); pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); + + return 0; +} + +/** + * pci_enable_acs - enable ACS if hardware support it + * @dev: the PCI device + */ +void pci_enable_acs(struct pci_dev *dev) +{ + if (!pci_acs_enable) + return; + + if (!pci_std_enable_acs(dev)) + return; + + pci_dev_specific_enable_acs(dev); } static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) @@ -4247,6 +4274,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) "Rounding up size of resource #%d to %#llx.\n", i, (unsigned long long)size); } + r->flags |= IORESOURCE_UNSET; r->end = size - 1; r->start = 0; } @@ -4260,6 +4288,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) r = &dev->resource[i]; if (!(r->flags & IORESOURCE_MEM)) continue; + r->flags |= IORESOURCE_UNSET; r->end = resource_size(r) - 1; r->start = 0; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4df38df224f4..6bd082299e31 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1,8 +1,6 @@ #ifndef DRIVERS_PCI_H #define DRIVERS_PCI_H -#include <linux/workqueue.h> - #define PCI_CFG_SPACE_SIZE 256 #define PCI_CFG_SPACE_EXP_SIZE 4096 @@ -240,8 +238,6 @@ struct pci_sriov { struct pci_dev *dev; /* lowest numbered PF */ struct pci_dev *self; /* this PF */ struct mutex lock; /* lock for VF bus */ - struct work_struct mtask; /* VF Migration task */ - u8 __iomem *mstate; /* VF Migration State Array */ }; #ifdef CONFIG_PCI_ATS diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6e34498ec9f0..ef09f5f2fe6c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -252,6 +252,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, /* Address above 32-bit boundary; disable the BAR */ pci_write_config_dword(dev, pos, 0); pci_write_config_dword(dev, pos + 4, 0); + res->flags |= IORESOURCE_UNSET; region.start = 0; region.end = sz64; bar_disabled = true; @@ -731,22 +732,6 @@ struct pci_bus *__ref pci_add_new_bus(struct pci_bus *parent, struct pci_dev *de return child; } -static void pci_fixup_parent_subordinate_busnr(struct pci_bus *child, int max) -{ - struct pci_bus *parent = child->parent; - - /* Attempts to fix that up are really dangerous unless - we're going to re-assign all bus numbers. */ - if (!pcibios_assign_all_busses()) - return; - - while (parent->parent && parent->busn_res.end < max) { - parent->busn_res.end = max; - pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, max); - parent = parent->parent; - } -} - /* * If it's a bridge, configure it and scan the bus behind it. * For CardBus bridges, we don't scan behind as the devices will @@ -782,7 +767,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) /* Check if setup is sensible at all */ if (!pass && (primary != bus->number || secondary <= bus->number || - secondary > subordinate)) { + secondary > subordinate || subordinate > bus->busn_res.end)) { dev_info(&dev->dev, "bridge configuration invalid ([bus %02x-%02x]), reconfiguring\n", secondary, subordinate); broken = 1; @@ -805,11 +790,10 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) goto out; /* - * If we already got to this bus through a different bridge, - * don't re-add it. This can happen with the i450NX chipset. - * - * However, we continue to descend down the hierarchy and - * scan remaining child buses. + * The bus might already exist for two reasons: Either we are + * rescanning the bus or the bus is reachable through more than + * one bridge. The second case can happen with the i450NX + * chipset. */ child = pci_find_bus(pci_domain_nr(bus), secondary); if (!child) { @@ -822,17 +806,19 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) } cmax = pci_scan_child_bus(child); - if (cmax > max) - max = cmax; - if (child->busn_res.end > max) - max = child->busn_res.end; + if (cmax > subordinate) + dev_warn(&dev->dev, "bridge has subordinate %02x but max busn %02x\n", + subordinate, cmax); + /* subordinate should equal child->busn_res.end */ + if (subordinate > max) + max = subordinate; } else { /* * We need to assign a number to this bus which we always * do in the second pass. */ if (!pass) { - if (pcibios_assign_all_busses() || broken) + if (pcibios_assign_all_busses() || broken || is_cardbus) /* Temporarily disable forwarding of the configuration cycles on all bridges in this bus segment to avoid possible @@ -844,19 +830,25 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) goto out; } + if (max >= bus->busn_res.end) { + dev_warn(&dev->dev, "can't allocate child bus %02x from %pR\n", + max, &bus->busn_res); + goto out; + } + /* Clear errors */ pci_write_config_word(dev, PCI_STATUS, 0xffff); - /* Prevent assigning a bus number that already exists. - * This can happen when a bridge is hot-plugged, so in - * this case we only re-scan this bus. */ + /* The bus will already exist if we are rescanning */ child = pci_find_bus(pci_domain_nr(bus), max+1); if (!child) { - child = pci_add_new_bus(bus, dev, ++max); + child = pci_add_new_bus(bus, dev, max+1); if (!child) goto out; - pci_bus_insert_busn_res(child, max, 0xff); + pci_bus_insert_busn_res(child, max+1, + bus->busn_res.end); } + max++; buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) | ((unsigned int)(child->busn_res.start) << 8) @@ -878,20 +870,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) if (!is_cardbus) { child->bridge_ctl = bctl; - /* - * Adjust subordinate busnr in parent buses. - * We do this before scanning for children because - * some devices may not be detected if the bios - * was lazy. - */ - pci_fixup_parent_subordinate_busnr(child, max); - /* Now we can scan all subordinate buses... */ max = pci_scan_child_bus(child); - /* - * now fix it up again since we have found - * the real value of max. - */ - pci_fixup_parent_subordinate_busnr(child, max); } else { /* * For CardBus bridges, we leave 4 bus numbers @@ -922,11 +901,15 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) } } max += i; - pci_fixup_parent_subordinate_busnr(child, max); } /* * Set the subordinate bus number to its real value. */ + if (max > bus->busn_res.end) { + dev_warn(&dev->dev, "max busn %02x is outside %pR\n", + max, &bus->busn_res); + max = bus->busn_res.end; + } pci_bus_update_busn_res_end(child, max); pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } @@ -1125,10 +1108,10 @@ int pci_setup_device(struct pci_dev *dev) pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device); /* - * Do the ugly legacy mode stuff here rather than broken chip - * quirk code. Legacy mode ATA controllers have fixed - * addresses. These are not always echoed in BAR0-3, and - * BAR0-3 in a few cases contain junk! + * Do the ugly legacy mode stuff here rather than broken chip + * quirk code. Legacy mode ATA controllers have fixed + * addresses. These are not always echoed in BAR0-3, and + * BAR0-3 in a few cases contain junk! */ if (class == PCI_CLASS_STORAGE_IDE) { u8 progif; @@ -1139,11 +1122,15 @@ int pci_setup_device(struct pci_dev *dev) res = &dev->resource[0]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x10: %pR\n", + res); region.start = 0x3F6; region.end = 0x3F6; res = &dev->resource[1]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x14: %pR\n", + res); } if ((progif & 4) == 0) { region.start = 0x170; @@ -1151,11 +1138,15 @@ int pci_setup_device(struct pci_dev *dev) res = &dev->resource[2]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x18: %pR\n", + res); region.start = 0x376; region.end = 0x376; res = &dev->resource[3]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x1c: %pR\n", + res); } } break; @@ -1835,7 +1826,7 @@ int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) res->flags |= IORESOURCE_PCI_FIXED; } - conflict = insert_resource_conflict(parent_res, res); + conflict = request_resource_conflict(parent_res, res); if (conflict) dev_printk(KERN_DEBUG, &b->dev, diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5cb726c193de..e7292065a1b1 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -296,6 +296,7 @@ static void quirk_s3_64M(struct pci_dev *dev) struct resource *r = &dev->resource[0]; if ((r->start & 0x3ffffff) || r->end != r->start + 0x3ffffff) { + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0x3ffffff; } @@ -937,6 +938,8 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C static void quirk_dunord(struct pci_dev *dev) { struct resource *r = &dev->resource [1]; + + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0xffffff; } @@ -1740,6 +1743,7 @@ static void quirk_tc86c001_ide(struct pci_dev *dev) struct resource *r = &dev->resource[0]; if (r->start & 0x8) { + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0xf; } @@ -1769,6 +1773,7 @@ static void quirk_plx_pci9050(struct pci_dev *dev) dev_info(&dev->dev, "Re-allocating PLX PCI 9050 BAR %u to length 256 to avoid bit 7 bug\n", bar); + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0xff; } @@ -3423,6 +3428,61 @@ static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags) #endif } +/* + * Many Intel PCH root ports do provide ACS-like features to disable peer + * transactions and validate bus numbers in requests, but do not provide an + * actual PCIe ACS capability. This is the list of device IDs known to fall + * into that category as provided by Intel in Red Hat bugzilla 1037684. + */ +static const u16 pci_quirk_intel_pch_acs_ids[] = { + /* Ibexpeak PCH */ + 0x3b42, 0x3b43, 0x3b44, 0x3b45, 0x3b46, 0x3b47, 0x3b48, 0x3b49, + 0x3b4a, 0x3b4b, 0x3b4c, 0x3b4d, 0x3b4e, 0x3b4f, 0x3b50, 0x3b51, + /* Cougarpoint PCH */ + 0x1c10, 0x1c11, 0x1c12, 0x1c13, 0x1c14, 0x1c15, 0x1c16, 0x1c17, + 0x1c18, 0x1c19, 0x1c1a, 0x1c1b, 0x1c1c, 0x1c1d, 0x1c1e, 0x1c1f, + /* Pantherpoint PCH */ + 0x1e10, 0x1e11, 0x1e12, 0x1e13, 0x1e14, 0x1e15, 0x1e16, 0x1e17, + 0x1e18, 0x1e19, 0x1e1a, 0x1e1b, 0x1e1c, 0x1e1d, 0x1e1e, 0x1e1f, + /* Lynxpoint-H PCH */ + 0x8c10, 0x8c11, 0x8c12, 0x8c13, 0x8c14, 0x8c15, 0x8c16, 0x8c17, + 0x8c18, 0x8c19, 0x8c1a, 0x8c1b, 0x8c1c, 0x8c1d, 0x8c1e, 0x8c1f, + /* Lynxpoint-LP PCH */ + 0x9c10, 0x9c11, 0x9c12, 0x9c13, 0x9c14, 0x9c15, 0x9c16, 0x9c17, + 0x9c18, 0x9c19, 0x9c1a, 0x9c1b, + /* Wildcat PCH */ + 0x9c90, 0x9c91, 0x9c92, 0x9c93, 0x9c94, 0x9c95, 0x9c96, 0x9c97, + 0x9c98, 0x9c99, 0x9c9a, 0x9c9b, +}; + +static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev) +{ + int i; + + /* Filter out a few obvious non-matches first */ + if (!pci_is_pcie(dev) || pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT) + return false; + + for (i = 0; i < ARRAY_SIZE(pci_quirk_intel_pch_acs_ids); i++) + if (pci_quirk_intel_pch_acs_ids[i] == dev->device) + return true; + + return false; +} + +#define INTEL_PCH_ACS_FLAGS (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_SV) + +static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags) +{ + u16 flags = dev->dev_flags & PCI_DEV_FLAGS_ACS_ENABLED_QUIRK ? + INTEL_PCH_ACS_FLAGS : 0; + + if (!pci_quirk_intel_pch_acs_match(dev)) + return -ENOTTY; + + return acs_flags & ~flags ? 0 : 1; +} + static const struct pci_dev_acs_enabled { u16 vendor; u16 device; @@ -3434,6 +3494,7 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_ATI, 0x439d, pci_quirk_amd_sb_acs }, { PCI_VENDOR_ID_ATI, 0x4384, pci_quirk_amd_sb_acs }, { PCI_VENDOR_ID_ATI, 0x4399, pci_quirk_amd_sb_acs }, + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, { 0 } }; @@ -3461,3 +3522,132 @@ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) return -ENOTTY; } + +/* Config space offset of Root Complex Base Address register */ +#define INTEL_LPC_RCBA_REG 0xf0 +/* 31:14 RCBA address */ +#define INTEL_LPC_RCBA_MASK 0xffffc000 +/* RCBA Enable */ +#define INTEL_LPC_RCBA_ENABLE (1 << 0) + +/* Backbone Scratch Pad Register */ +#define INTEL_BSPR_REG 0x1104 +/* Backbone Peer Non-Posted Disable */ +#define INTEL_BSPR_REG_BPNPD (1 << 8) +/* Backbone Peer Posted Disable */ +#define INTEL_BSPR_REG_BPPD (1 << 9) + +/* Upstream Peer Decode Configuration Register */ +#define INTEL_UPDCR_REG 0x1114 +/* 5:0 Peer Decode Enable bits */ +#define INTEL_UPDCR_REG_MASK 0x3f + +static int pci_quirk_enable_intel_lpc_acs(struct pci_dev *dev) +{ + u32 rcba, bspr, updcr; + void __iomem *rcba_mem; + + /* + * Read the RCBA register from the LPC (D31:F0). PCH root ports + * are D28:F* and therefore get probed before LPC, thus we can't + * use pci_get_slot/pci_read_config_dword here. + */ + pci_bus_read_config_dword(dev->bus, PCI_DEVFN(31, 0), + INTEL_LPC_RCBA_REG, &rcba); + if (!(rcba & INTEL_LPC_RCBA_ENABLE)) + return -EINVAL; + + rcba_mem = ioremap_nocache(rcba & INTEL_LPC_RCBA_MASK, + PAGE_ALIGN(INTEL_UPDCR_REG)); + if (!rcba_mem) + return -ENOMEM; + + /* + * The BSPR can disallow peer cycles, but it's set by soft strap and + * therefore read-only. If both posted and non-posted peer cycles are + * disallowed, we're ok. If either are allowed, then we need to use + * the UPDCR to disable peer decodes for each port. This provides the + * PCIe ACS equivalent of PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF + */ + bspr = readl(rcba_mem + INTEL_BSPR_REG); + bspr &= INTEL_BSPR_REG_BPNPD | INTEL_BSPR_REG_BPPD; + if (bspr != (INTEL_BSPR_REG_BPNPD | INTEL_BSPR_REG_BPPD)) { + updcr = readl(rcba_mem + INTEL_UPDCR_REG); + if (updcr & INTEL_UPDCR_REG_MASK) { + dev_info(&dev->dev, "Disabling UPDCR peer decodes\n"); + updcr &= ~INTEL_UPDCR_REG_MASK; + writel(updcr, rcba_mem + INTEL_UPDCR_REG); + } + } + + iounmap(rcba_mem); + return 0; +} + +/* Miscellaneous Port Configuration register */ +#define INTEL_MPC_REG 0xd8 +/* MPC: Invalid Receive Bus Number Check Enable */ +#define INTEL_MPC_REG_IRBNCE (1 << 26) + +static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev) +{ + u32 mpc; + + /* + * When enabled, the IRBNCE bit of the MPC register enables the + * equivalent of PCI ACS Source Validation (PCI_ACS_SV), which + * ensures that requester IDs fall within the bus number range + * of the bridge. Enable if not already. + */ + pci_read_config_dword(dev, INTEL_MPC_REG, &mpc); + if (!(mpc & INTEL_MPC_REG_IRBNCE)) { + dev_info(&dev->dev, "Enabling MPC IRBNCE\n"); + mpc |= INTEL_MPC_REG_IRBNCE; + pci_write_config_word(dev, INTEL_MPC_REG, mpc); + } +} + +static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev) +{ + if (!pci_quirk_intel_pch_acs_match(dev)) + return -ENOTTY; + + if (pci_quirk_enable_intel_lpc_acs(dev)) { + dev_warn(&dev->dev, "Failed to enable Intel PCH ACS quirk\n"); + return 0; + } + + pci_quirk_enable_intel_rp_mpc_acs(dev); + + dev->dev_flags |= PCI_DEV_FLAGS_ACS_ENABLED_QUIRK; + + dev_info(&dev->dev, "Intel PCH root port ACS workaround enabled\n"); + + return 0; +} + +static const struct pci_dev_enable_acs { + u16 vendor; + u16 device; + int (*enable_acs)(struct pci_dev *dev); +} pci_dev_enable_acs[] = { + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_pch_acs }, + { 0 } +}; + +void pci_dev_specific_enable_acs(struct pci_dev *dev) +{ + const struct pci_dev_enable_acs *i; + int ret; + + for (i = pci_dev_enable_acs; i->enable_acs; i++) { + if ((i->vendor == dev->vendor || + i->vendor == (u16)PCI_ANY_ID) && + (i->device == dev->device || + i->device == (u16)PCI_ANY_ID)) { + ret = i->enable_acs(dev); + if (ret >= 0) + return; + } + } +} diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 5d595724e5f4..c1839450d4d6 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -197,8 +197,10 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom) void pci_cleanup_rom(struct pci_dev *pdev) { struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; + if (res->flags & IORESOURCE_ROM_COPY) { kfree((void*)(unsigned long)res->start); + res->flags |= IORESOURCE_UNSET; res->flags &= ~IORESOURCE_ROM_COPY; res->start = 0; res->end = 0; diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 3ff2ac7c14e2..4a1b972efe7f 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -54,14 +54,14 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev) static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr) { - struct pci_bus* child; - struct list_head *tmp; + struct pci_bus *child; + struct pci_bus *tmp; if(bus->number == busnr) return bus; - list_for_each(tmp, &bus->children) { - child = pci_do_find_bus(pci_bus_b(tmp), busnr); + list_for_each_entry(tmp, &bus->children, node) { + child = pci_do_find_bus(tmp, busnr); if(child) return child; } @@ -111,7 +111,7 @@ pci_find_next_bus(const struct pci_bus *from) down_read(&pci_bus_sem); n = from ? from->node.next : pci_root_buses.next; if (n != &pci_root_buses) - b = pci_bus_b(n); + b = list_entry(n, struct pci_bus, node); up_read(&pci_bus_sem); return b; } diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 5c060b152ce6..7eed671d5586 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -44,6 +44,9 @@ void pci_update_resource(struct pci_dev *dev, int resno) if (!res->flags) return; + if (res->flags & IORESOURCE_UNSET) + return; + /* * Ignore non-moveable resources. This might be legacy resources for * which no functional BAR register exists or another important @@ -101,11 +104,6 @@ void pci_update_resource(struct pci_dev *dev, int resno) if (disable) pci_write_config_word(dev, PCI_COMMAND, cmd); - - res->flags &= ~IORESOURCE_UNSET; - dev_dbg(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n", - resno, res, (unsigned long long)region.start, - (unsigned long long)region.end); } int pci_claim_resource(struct pci_dev *dev, int resource) @@ -113,18 +111,23 @@ int pci_claim_resource(struct pci_dev *dev, int resource) struct resource *res = &dev->resource[resource]; struct resource *root, *conflict; + if (res->flags & IORESOURCE_UNSET) { + dev_info(&dev->dev, "can't claim BAR %d %pR: no address assigned\n", + resource, res); + return -EINVAL; + } + root = pci_find_parent_resource(dev, res); if (!root) { - dev_info(&dev->dev, "no compatible bridge window for %pR\n", - res); + dev_info(&dev->dev, "can't claim BAR %d %pR: no compatible bridge window\n", + resource, res); return -EINVAL; } conflict = request_resource_conflict(root, res); if (conflict) { - dev_info(&dev->dev, - "address space collision: %pR conflicts with %s %pR\n", - res, conflict->name, conflict); + dev_info(&dev->dev, "can't claim BAR %d %pR: address conflict with %s %pR\n", + resource, res, conflict->name, conflict); return -EBUSY; } @@ -263,6 +266,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) resource_size_t align, size; int ret; + res->flags |= IORESOURCE_UNSET; align = pci_resource_alignment(dev, res); if (!align) { dev_info(&dev->dev, "BAR %d: can't assign %pR " @@ -282,6 +286,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) ret = pci_revert_fw_address(res, dev, resno, size); if (!ret) { + res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res); if (resno < PCI_BRIDGE_RESOURCES) @@ -297,6 +302,7 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz resource_size_t new_size; int ret; + res->flags |= IORESOURCE_UNSET; if (!res->parent) { dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR " "\n", resno, res); @@ -307,6 +313,7 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz new_size = resource_size(res) + addsize; ret = _pci_assign_resource(dev, resno, new_size, min_align); if (!ret) { + res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; dev_info(&dev->dev, "BAR %d: reassigned %pR\n", resno, res); if (resno < PCI_BRIDGE_RESOURCES) @@ -336,9 +343,15 @@ int pci_enable_resources(struct pci_dev *dev, int mask) (!(r->flags & IORESOURCE_ROM_ENABLE))) continue; + if (r->flags & IORESOURCE_UNSET) { + dev_err(&dev->dev, "can't enable device: BAR %d %pR not assigned\n", + i, r); + return -EINVAL; + } + if (!r->parent) { - dev_err(&dev->dev, "device not available " - "(can't reserve %pR)\n", r); + dev_err(&dev->dev, "can't enable device: BAR %d %pR not claimed\n", + i, r); return -EINVAL; } diff --git a/drivers/pcmcia/sa11xx_base.c b/drivers/pcmcia/sa11xx_base.c index 6eecd7cddf57..54d3089d157b 100644 --- a/drivers/pcmcia/sa11xx_base.c +++ b/drivers/pcmcia/sa11xx_base.c @@ -125,9 +125,6 @@ sa1100_pcmcia_frequency_change(struct soc_pcmcia_socket *skt, if (freqs->new < freqs->old) sa1100_pcmcia_set_mecr(skt, freqs->new); break; - case CPUFREQ_RESUMECHANGE: - sa1100_pcmcia_set_mecr(skt, freqs->new); - break; } return 0; diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 8485761e76af..946f90ef6020 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1076,7 +1076,7 @@ static void yenta_config_init(struct yenta_socket *socket) */ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge) { - struct list_head *tmp; + struct pci_bus *sibling; unsigned char upper_limit; /* * We only check and fix the parent bridge: All systems which need @@ -1095,18 +1095,18 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge) /* stay within the limits of the bus range of the parent: */ upper_limit = bridge_to_fix->parent->busn_res.end; - /* check the bus ranges of all silbling bridges to prevent overlap */ - list_for_each(tmp, &bridge_to_fix->parent->children) { - struct pci_bus *silbling = pci_bus_b(tmp); + /* check the bus ranges of all sibling bridges to prevent overlap */ + list_for_each_entry(sibling, &bridge_to_fix->parent->children, + node) { /* - * If the silbling has a higher secondary bus number + * If the sibling has a higher secondary bus number * and it's secondary is equal or smaller than our * current upper limit, set the new upper limit to - * the bus number below the silbling's range: + * the bus number below the sibling's range: */ - if (silbling->busn_res.start > bridge_to_fix->busn_res.end - && silbling->busn_res.start <= upper_limit) - upper_limit = silbling->busn_res.start - 1; + if (sibling->busn_res.start > bridge_to_fix->busn_res.end + && sibling->busn_res.start <= upper_limit) + upper_limit = sibling->busn_res.start - 1; } /* Show that the wanted subordinate number is not possible: */ diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index be361b7cd30f..06cee0189f3e 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -217,14 +217,14 @@ config PINCTRL_IMX28 select PINCTRL_MXS config PINCTRL_MSM - tristate + bool select PINMUX select PINCONF select GENERIC_PINCONF config PINCTRL_MSM8X74 tristate "Qualcomm 8x74 pin controller driver" - depends on GPIOLIB && OF && OF_IRQ + depends on GPIOLIB && OF select PINCTRL_MSM help This is the pinctrl, pinmux, pinconf and gpiolib driver for the diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index 340fb4e6c600..eda13de2e7c0 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -186,7 +186,9 @@ int pinctrl_dt_to_map(struct pinctrl *p) /* CONFIG_OF enabled, p->dev not instantiated from DT */ if (!np) { - dev_dbg(p->dev, "no of_node; not parsing pinctrl DT\n"); + if (of_have_populated_dt()) + dev_dbg(p->dev, + "no of_node; not parsing pinctrl DT\n"); return 0; } diff --git a/drivers/pinctrl/mvebu/Kconfig b/drivers/pinctrl/mvebu/Kconfig index 366fa541ee91..cc298fade93a 100644 --- a/drivers/pinctrl/mvebu/Kconfig +++ b/drivers/pinctrl/mvebu/Kconfig @@ -8,6 +8,7 @@ config PINCTRL_MVEBU config PINCTRL_DOVE bool select PINCTRL_MVEBU + select MFD_SYSCON config PINCTRL_KIRKWOOD bool @@ -17,6 +18,14 @@ config PINCTRL_ARMADA_370 bool select PINCTRL_MVEBU +config PINCTRL_ARMADA_375 + bool + select PINCTRL_MVEBU + +config PINCTRL_ARMADA_38X + bool + select PINCTRL_MVEBU + config PINCTRL_ARMADA_XP bool select PINCTRL_MVEBU diff --git a/drivers/pinctrl/mvebu/Makefile b/drivers/pinctrl/mvebu/Makefile index 37c253297af0..bc1b9f14f539 100644 --- a/drivers/pinctrl/mvebu/Makefile +++ b/drivers/pinctrl/mvebu/Makefile @@ -2,4 +2,6 @@ obj-$(CONFIG_PINCTRL_MVEBU) += pinctrl-mvebu.o obj-$(CONFIG_PINCTRL_DOVE) += pinctrl-dove.o obj-$(CONFIG_PINCTRL_KIRKWOOD) += pinctrl-kirkwood.o obj-$(CONFIG_PINCTRL_ARMADA_370) += pinctrl-armada-370.o +obj-$(CONFIG_PINCTRL_ARMADA_375) += pinctrl-armada-375.o +obj-$(CONFIG_PINCTRL_ARMADA_38X) += pinctrl-armada-38x.o obj-$(CONFIG_PINCTRL_ARMADA_XP) += pinctrl-armada-xp.o diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-370.c b/drivers/pinctrl/mvebu/pinctrl-armada-370.c index ae1f760cbdd2..670e5b01c678 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-370.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-370.c @@ -23,6 +23,18 @@ #include "pinctrl-mvebu.h" +static void __iomem *mpp_base; + +static int armada_370_mpp_ctrl_get(unsigned pid, unsigned long *config) +{ + return default_mpp_ctrl_get(mpp_base, pid, config); +} + +static int armada_370_mpp_ctrl_set(unsigned pid, unsigned long config) +{ + return default_mpp_ctrl_set(mpp_base, pid, config); +} + static struct mvebu_mpp_mode mv88f6710_mpp_modes[] = { MPP_MODE(0, MPP_FUNCTION(0x0, "gpio", NULL), @@ -373,7 +385,7 @@ static struct of_device_id armada_370_pinctrl_of_match[] = { }; static struct mvebu_mpp_ctrl mv88f6710_mpp_controls[] = { - MPP_REG_CTRL(0, 65), + MPP_FUNC_CTRL(0, 65, NULL, armada_370_mpp_ctrl), }; static struct pinctrl_gpio_range mv88f6710_mpp_gpio_ranges[] = { @@ -385,6 +397,12 @@ static struct pinctrl_gpio_range mv88f6710_mpp_gpio_ranges[] = { static int armada_370_pinctrl_probe(struct platform_device *pdev) { struct mvebu_pinctrl_soc_info *soc = &armada_370_pinctrl_info; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mpp_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mpp_base)) + return PTR_ERR(mpp_base); soc->variant = 0; /* no variants for Armada 370 */ soc->controls = mv88f6710_mpp_controls; diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-375.c b/drivers/pinctrl/mvebu/pinctrl-armada-375.c new file mode 100644 index 000000000000..db078fe7ace6 --- /dev/null +++ b/drivers/pinctrl/mvebu/pinctrl-armada-375.c @@ -0,0 +1,459 @@ +/* + * Marvell Armada 375 pinctrl driver based on mvebu pinctrl core + * + * Copyright (C) 2012 Marvell + * + * Thomas Petazzoni <thomas.petazzoni@free-electrons.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/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinctrl.h> + +#include "pinctrl-mvebu.h" + +static void __iomem *mpp_base; + +static int armada_375_mpp_ctrl_get(unsigned pid, unsigned long *config) +{ + return default_mpp_ctrl_get(mpp_base, pid, config); +} + +static int armada_375_mpp_ctrl_set(unsigned pid, unsigned long config) +{ + return default_mpp_ctrl_set(mpp_base, pid, config); +} + +static struct mvebu_mpp_mode mv88f6720_mpp_modes[] = { + MPP_MODE(0, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "ad2"), + MPP_FUNCTION(0x2, "spi0", "cs1"), + MPP_FUNCTION(0x3, "spi1", "cs1"), + MPP_FUNCTION(0x5, "nand", "io2")), + MPP_MODE(1, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "ad3"), + MPP_FUNCTION(0x2, "spi0", "mosi"), + MPP_FUNCTION(0x3, "spi1", "mosi"), + MPP_FUNCTION(0x5, "nand", "io3")), + MPP_MODE(2, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "ad4"), + MPP_FUNCTION(0x2, "ptp", "eventreq"), + MPP_FUNCTION(0x3, "led", "c0"), + MPP_FUNCTION(0x4, "audio", "sdi"), + MPP_FUNCTION(0x5, "nand", "io4"), + MPP_FUNCTION(0x6, "spi1", "mosi")), + MPP_MODE(3, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "ad5"), + MPP_FUNCTION(0x2, "ptp", "triggen"), + MPP_FUNCTION(0x3, "led", "p3"), + MPP_FUNCTION(0x4, "audio", "mclk"), + MPP_FUNCTION(0x5, "nand", "io5"), + MPP_FUNCTION(0x6, "spi1", "miso")), + MPP_MODE(4, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "ad6"), + MPP_FUNCTION(0x2, "spi0", "miso"), + MPP_FUNCTION(0x3, "spi1", "miso"), + MPP_FUNCTION(0x5, "nand", "io6")), + MPP_MODE(5, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "ad7"), + MPP_FUNCTION(0x2, "spi0", "cs2"), + MPP_FUNCTION(0x3, "spi1", "cs2"), + MPP_FUNCTION(0x5, "nand", "io7"), + MPP_FUNCTION(0x6, "spi1", "miso")), + MPP_MODE(6, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "ad0"), + MPP_FUNCTION(0x3, "led", "p1"), + MPP_FUNCTION(0x4, "audio", "rclk"), + MPP_FUNCTION(0x5, "nand", "io0")), + MPP_MODE(7, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "ad1"), + MPP_FUNCTION(0x2, "ptp", "clk"), + MPP_FUNCTION(0x3, "led", "p2"), + MPP_FUNCTION(0x4, "audio", "extclk"), + MPP_FUNCTION(0x5, "nand", "io1")), + MPP_MODE(8, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev ", "bootcs"), + MPP_FUNCTION(0x2, "spi0", "cs0"), + MPP_FUNCTION(0x3, "spi1", "cs0"), + MPP_FUNCTION(0x5, "nand", "ce")), + MPP_MODE(9, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "nf", "wen"), + MPP_FUNCTION(0x2, "spi0", "sck"), + MPP_FUNCTION(0x3, "spi1", "sck"), + MPP_FUNCTION(0x5, "nand", "we")), + MPP_MODE(10, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "nf", "ren"), + MPP_FUNCTION(0x2, "dram", "vttctrl"), + MPP_FUNCTION(0x3, "led", "c1"), + MPP_FUNCTION(0x5, "nand", "re"), + MPP_FUNCTION(0x6, "spi1", "sck")), + MPP_MODE(11, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "a0"), + MPP_FUNCTION(0x3, "led", "c2"), + MPP_FUNCTION(0x4, "audio", "sdo"), + MPP_FUNCTION(0x5, "nand", "cle")), + MPP_MODE(12, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "a1"), + MPP_FUNCTION(0x4, "audio", "bclk"), + MPP_FUNCTION(0x5, "nand", "ale")), + MPP_MODE(13, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "dev", "readyn"), + MPP_FUNCTION(0x2, "pcie0", "rstoutn"), + MPP_FUNCTION(0x3, "pcie1", "rstoutn"), + MPP_FUNCTION(0x5, "nand", "rb"), + MPP_FUNCTION(0x6, "spi1", "mosi")), + MPP_MODE(14, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "i2c0", "sda"), + MPP_FUNCTION(0x3, "uart1", "txd")), + MPP_MODE(15, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "i2c0", "sck"), + MPP_FUNCTION(0x3, "uart1", "rxd")), + MPP_MODE(16, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "uart0", "txd")), + MPP_MODE(17, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "uart0", "rxd")), + MPP_MODE(18, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "tdm", "intn")), + MPP_MODE(19, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "tdm", "rstn")), + MPP_MODE(20, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "tdm", "pclk")), + MPP_MODE(21, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "tdm", "fsync")), + MPP_MODE(22, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "tdm", "drx")), + MPP_MODE(23, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "tdm", "dtx")), + MPP_MODE(24, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "led", "p0"), + MPP_FUNCTION(0x2, "ge1", "rxd0"), + MPP_FUNCTION(0x3, "sd", "cmd"), + MPP_FUNCTION(0x4, "uart0", "rts"), + MPP_FUNCTION(0x5, "spi0", "cs0"), + MPP_FUNCTION(0x6, "dev", "cs1")), + MPP_MODE(25, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "led", "p2"), + MPP_FUNCTION(0x2, "ge1", "rxd1"), + MPP_FUNCTION(0x3, "sd", "d0"), + MPP_FUNCTION(0x4, "uart0", "cts"), + MPP_FUNCTION(0x5, "spi0", "mosi"), + MPP_FUNCTION(0x6, "dev", "cs2")), + MPP_MODE(26, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "pcie0", "clkreq"), + MPP_FUNCTION(0x2, "ge1", "rxd2"), + MPP_FUNCTION(0x3, "sd", "d2"), + MPP_FUNCTION(0x4, "uart1", "rts"), + MPP_FUNCTION(0x5, "spi0", "cs1"), + MPP_FUNCTION(0x6, "led", "c1")), + MPP_MODE(27, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "pcie1", "clkreq"), + MPP_FUNCTION(0x2, "ge1", "rxd3"), + MPP_FUNCTION(0x3, "sd", "d1"), + MPP_FUNCTION(0x4, "uart1", "cts"), + MPP_FUNCTION(0x5, "spi0", "miso"), + MPP_FUNCTION(0x6, "led", "c2")), + MPP_MODE(28, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "led", "p3"), + MPP_FUNCTION(0x2, "ge1", "txctl"), + MPP_FUNCTION(0x3, "sd", "clk"), + MPP_FUNCTION(0x5, "dram", "vttctrl")), + MPP_MODE(29, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "pcie1", "clkreq"), + MPP_FUNCTION(0x2, "ge1", "rxclk"), + MPP_FUNCTION(0x3, "sd", "d3"), + MPP_FUNCTION(0x5, "spi0", "sck"), + MPP_FUNCTION(0x6, "pcie0", "rstoutn")), + MPP_MODE(30, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ge1", "txd0"), + MPP_FUNCTION(0x3, "spi1", "cs0"), + MPP_FUNCTION(0x5, "led", "p3"), + MPP_FUNCTION(0x6, "ptp", "eventreq")), + MPP_MODE(31, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ge1", "txd1"), + MPP_FUNCTION(0x3, "spi1", "mosi"), + MPP_FUNCTION(0x5, "led", "p0")), + MPP_MODE(32, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ge1", "txd2"), + MPP_FUNCTION(0x3, "spi1", "sck"), + MPP_FUNCTION(0x4, "ptp", "triggen"), + MPP_FUNCTION(0x5, "led", "c0")), + MPP_MODE(33, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ge1", "txd3"), + MPP_FUNCTION(0x3, "spi1", "miso"), + MPP_FUNCTION(0x5, "led", "p2")), + MPP_MODE(34, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ge1", "txclkout"), + MPP_FUNCTION(0x3, "spi1", "sck"), + MPP_FUNCTION(0x5, "led", "c1")), + MPP_MODE(35, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ge1", "rxctl"), + MPP_FUNCTION(0x3, "spi1", "cs1"), + MPP_FUNCTION(0x4, "spi0", "cs2"), + MPP_FUNCTION(0x5, "led", "p1")), + MPP_MODE(36, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "pcie0", "clkreq"), + MPP_FUNCTION(0x5, "led", "c2")), + MPP_MODE(37, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "pcie0", "clkreq"), + MPP_FUNCTION(0x2, "tdm", "intn"), + MPP_FUNCTION(0x4, "ge", "mdc")), + MPP_MODE(38, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "pcie1", "clkreq"), + MPP_FUNCTION(0x4, "ge", "mdio")), + MPP_MODE(39, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x4, "ref", "clkout"), + MPP_FUNCTION(0x5, "led", "p3")), + MPP_MODE(40, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x4, "uart1", "txd"), + MPP_FUNCTION(0x5, "led", "p0")), + MPP_MODE(41, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x4, "uart1", "rxd"), + MPP_FUNCTION(0x5, "led", "p1")), + MPP_MODE(42, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x3, "spi1", "cs2"), + MPP_FUNCTION(0x4, "led", "c0"), + MPP_FUNCTION(0x6, "ptp", "clk")), + MPP_MODE(43, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "sata0", "prsnt"), + MPP_FUNCTION(0x4, "dram", "vttctrl"), + MPP_FUNCTION(0x5, "led", "c1")), + MPP_MODE(44, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x4, "sata0", "prsnt")), + MPP_MODE(45, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "spi0", "cs2"), + MPP_FUNCTION(0x4, "pcie0", "rstoutn"), + MPP_FUNCTION(0x5, "led", "c2"), + MPP_FUNCTION(0x6, "spi1", "cs2")), + MPP_MODE(46, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "led", "p0"), + MPP_FUNCTION(0x2, "ge0", "txd0"), + MPP_FUNCTION(0x3, "ge1", "txd0"), + MPP_FUNCTION(0x6, "dev", "wen1")), + MPP_MODE(47, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "led", "p1"), + MPP_FUNCTION(0x2, "ge0", "txd1"), + MPP_FUNCTION(0x3, "ge1", "txd1"), + MPP_FUNCTION(0x5, "ptp", "triggen"), + MPP_FUNCTION(0x6, "dev", "ale0")), + MPP_MODE(48, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "led", "p2"), + MPP_FUNCTION(0x2, "ge0", "txd2"), + MPP_FUNCTION(0x3, "ge1", "txd2"), + MPP_FUNCTION(0x6, "dev", "ale1")), + MPP_MODE(49, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "led", "p3"), + MPP_FUNCTION(0x2, "ge0", "txd3"), + MPP_FUNCTION(0x3, "ge1", "txd3"), + MPP_FUNCTION(0x6, "dev", "a2")), + MPP_MODE(50, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "led", "c0"), + MPP_FUNCTION(0x2, "ge0", "rxd0"), + MPP_FUNCTION(0x3, "ge1", "rxd0"), + MPP_FUNCTION(0x5, "ptp", "eventreq"), + MPP_FUNCTION(0x6, "dev", "ad12")), + MPP_MODE(51, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "led", "c1"), + MPP_FUNCTION(0x2, "ge0", "rxd1"), + MPP_FUNCTION(0x3, "ge1", "rxd1"), + MPP_FUNCTION(0x6, "dev", "ad8")), + MPP_MODE(52, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "led", "c2"), + MPP_FUNCTION(0x2, "ge0", "rxd2"), + MPP_FUNCTION(0x3, "ge1", "rxd2"), + MPP_FUNCTION(0x5, "i2c0", "sda"), + MPP_FUNCTION(0x6, "dev", "ad9")), + MPP_MODE(53, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "pcie1", "rstoutn"), + MPP_FUNCTION(0x2, "ge0", "rxd3"), + MPP_FUNCTION(0x3, "ge1", "rxd3"), + MPP_FUNCTION(0x5, "i2c0", "sck"), + MPP_FUNCTION(0x6, "dev", "ad10")), + MPP_MODE(54, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "pcie0", "rstoutn"), + MPP_FUNCTION(0x2, "ge0", "rxctl"), + MPP_FUNCTION(0x3, "ge1", "rxctl"), + MPP_FUNCTION(0x6, "dev", "ad11")), + MPP_MODE(55, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ge0", "rxclk"), + MPP_FUNCTION(0x3, "ge1", "rxclk"), + MPP_FUNCTION(0x6, "dev", "cs0")), + MPP_MODE(56, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ge0", "txclkout"), + MPP_FUNCTION(0x3, "ge1", "txclkout"), + MPP_FUNCTION(0x6, "dev", "oe")), + MPP_MODE(57, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ge0", "txctl"), + MPP_FUNCTION(0x3, "ge1", "txctl"), + MPP_FUNCTION(0x6, "dev", "wen0")), + MPP_MODE(58, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x4, "led", "c0")), + MPP_MODE(59, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x4, "led", "c1")), + MPP_MODE(60, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "uart1", "txd"), + MPP_FUNCTION(0x4, "led", "c2"), + MPP_FUNCTION(0x6, "dev", "ad13")), + MPP_MODE(61, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "i2c1", "sda"), + MPP_FUNCTION(0x2, "uart1", "rxd"), + MPP_FUNCTION(0x3, "spi1", "cs2"), + MPP_FUNCTION(0x4, "led", "p0"), + MPP_FUNCTION(0x6, "dev", "ad14")), + MPP_MODE(62, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "i2c1", "sck"), + MPP_FUNCTION(0x4, "led", "p1"), + MPP_FUNCTION(0x6, "dev", "ad15")), + MPP_MODE(63, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ptp", "triggen"), + MPP_FUNCTION(0x4, "led", "p2"), + MPP_FUNCTION(0x6, "dev", "burst")), + MPP_MODE(64, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "dram", "vttctrl"), + MPP_FUNCTION(0x4, "led", "p3")), + MPP_MODE(65, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x1, "sata1", "prsnt")), + MPP_MODE(66, + MPP_FUNCTION(0x0, "gpio", NULL), + MPP_FUNCTION(0x2, "ptp", "eventreq"), + MPP_FUNCTION(0x4, "spi1", "cs3"), + MPP_FUNCTION(0x5, "pcie0", "rstoutn"), + MPP_FUNCTION(0x6, "dev", "cs3")), +}; + +static struct mvebu_pinctrl_soc_info armada_375_pinctrl_info; + +static struct of_device_id armada_375_pinctrl_of_match[] = { + { .compatible = "marvell,mv88f6720-pinctrl" }, + { }, +}; + +static struct mvebu_mpp_ctrl mv88f6720_mpp_controls[] = { + MPP_FUNC_CTRL(0, 69, NULL, armada_375_mpp_ctrl), +}; + +static struct pinctrl_gpio_range mv88f6720_mpp_gpio_ranges[] = { + MPP_GPIO_RANGE(0, 0, 0, 32), + MPP_GPIO_RANGE(1, 32, 32, 32), + MPP_GPIO_RANGE(2, 64, 64, 3), +}; + +static int armada_375_pinctrl_probe(struct platform_device *pdev) +{ + struct mvebu_pinctrl_soc_info *soc = &armada_375_pinctrl_info; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mpp_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mpp_base)) + return PTR_ERR(mpp_base); + + soc->variant = 0; /* no variants for Armada 375 */ + soc->controls = mv88f6720_mpp_controls; + soc->ncontrols = ARRAY_SIZE(mv88f6720_mpp_controls); + soc->modes = mv88f6720_mpp_modes; + soc->nmodes = ARRAY_SIZE(mv88f6720_mpp_modes); + soc->gpioranges = mv88f6720_mpp_gpio_ranges; + soc->ngpioranges = ARRAY_SIZE(mv88f6720_mpp_gpio_ranges); + + pdev->dev.platform_data = soc; + + return mvebu_pinctrl_probe(pdev); +} + +static int armada_375_pinctrl_remove(struct platform_device *pdev) +{ + return mvebu_pinctrl_remove(pdev); +} + +static struct platform_driver armada_375_pinctrl_driver = { + .driver = { + .name = "armada-375-pinctrl", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(armada_375_pinctrl_of_match), + }, + .probe = armada_375_pinctrl_probe, + .remove = armada_375_pinctrl_remove, +}; + +module_platform_driver(armada_375_pinctrl_driver); + +MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); +MODULE_DESCRIPTION("Marvell Armada 375 pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-38x.c b/drivers/pinctrl/mvebu/pinctrl-armada-38x.c new file mode 100644 index 000000000000..1049f82fb62f --- /dev/null +++ b/drivers/pinctrl/mvebu/pinctrl-armada-38x.c @@ -0,0 +1,462 @@ +/* + * Marvell Armada 380/385 pinctrl driver based on mvebu pinctrl core + * + * Copyright (C) 2013 Marvell + * + * Thomas Petazzoni <thomas.petazzoni@free-electrons.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/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinctrl.h> + +#include "pinctrl-mvebu.h" + +static void __iomem *mpp_base; + +static int armada_38x_mpp_ctrl_get(unsigned pid, unsigned long *config) +{ + return default_mpp_ctrl_get(mpp_base, pid, config); +} + +static int armada_38x_mpp_ctrl_set(unsigned pid, unsigned long config) +{ + return default_mpp_ctrl_set(mpp_base, pid, config); +} + +enum { + V_88F6810 = BIT(0), + V_88F6820 = BIT(1), + V_88F6828 = BIT(2), + V_88F6810_PLUS = (V_88F6810 | V_88F6820 | V_88F6828), + V_88F6820_PLUS = (V_88F6820 | V_88F6828), +}; + +static struct mvebu_mpp_mode armada_38x_mpp_modes[] = { + MPP_MODE(0, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua0", "rxd", V_88F6810_PLUS)), + MPP_MODE(1, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua0", "txd", V_88F6810_PLUS)), + MPP_MODE(2, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "i2c0", "sck", V_88F6810_PLUS)), + MPP_MODE(3, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "i2c0", "sda", V_88F6810_PLUS)), + MPP_MODE(4, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge", "mdc", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ua1", "txd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "rts", V_88F6810_PLUS)), + MPP_MODE(5, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge", "mdio", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ua1", "rxd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "cts", V_88F6810_PLUS)), + MPP_MODE(6, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txclkout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge0", "crs", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs3", V_88F6810_PLUS)), + MPP_MODE(7, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txd0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad9", V_88F6810_PLUS)), + MPP_MODE(8, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txd1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad10", V_88F6810_PLUS)), + MPP_MODE(9, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txd2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad11", V_88F6810_PLUS)), + MPP_MODE(10, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txd3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad12", V_88F6810_PLUS)), + MPP_MODE(11, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txctl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad13", V_88F6810_PLUS)), + MPP_MODE(12, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxd0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie1", "rstout", V_88F6820_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "cs1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad14", V_88F6810_PLUS)), + MPP_MODE(13, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxd1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "clkreq", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie1", "clkreq", V_88F6820_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "cs2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad15", V_88F6810_PLUS)), + MPP_MODE(14, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxd2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ptp", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "m", "vtt_ctrl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "cs3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "wen1", V_88F6810_PLUS)), + MPP_MODE(15, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxd3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge", "mdc slave", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "mosi", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "pcie1", "rstout", V_88F6820_PLUS)), + MPP_MODE(16, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxctl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge", "mdio slave", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "m", "decc_err", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "miso", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "pcie0", "clkreq", V_88F6810_PLUS)), + MPP_MODE(17, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ptp", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "rxd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sata1", "prsnt", V_88F6810_PLUS)), + MPP_MODE(18, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "rxerr", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ptp", "trig_gen", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "txd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "cs0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "pcie1", "rstout", V_88F6820_PLUS)), + MPP_MODE(19, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "col", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ptp", "event_req", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie0", "clkreq", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "ua0", "cts", V_88F6810_PLUS)), + MPP_MODE(20, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ge0", "txclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ptp", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie1", "rstout", V_88F6820_PLUS), + MPP_VAR_FUNCTION(4, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "ua0", "rts", V_88F6810_PLUS)), + MPP_MODE(21, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxd0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "cmd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "bootcs", V_88F6810_PLUS)), + MPP_MODE(22, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "mosi", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad0", V_88F6810_PLUS)), + MPP_MODE(23, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad2", V_88F6810_PLUS)), + MPP_MODE(24, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "miso", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ua0", "cts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "rxd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d4", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ready", V_88F6810_PLUS)), + MPP_MODE(25, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ua0", "rts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "txd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d5", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs0", V_88F6810_PLUS)), + MPP_MODE(26, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "i2c1", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d6", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs1", V_88F6810_PLUS)), + MPP_MODE(27, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txclkout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "i2c1", "sda", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d7", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs2", V_88F6810_PLUS)), + MPP_MODE(28, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txd0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad5", V_88F6810_PLUS)), + MPP_MODE(29, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txd1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ale0", V_88F6810_PLUS)), + MPP_MODE(30, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txd2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "oen", V_88F6810_PLUS)), + MPP_MODE(31, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txd3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ale1", V_88F6810_PLUS)), + MPP_MODE(32, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "txctl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "wen0", V_88F6810_PLUS)), + MPP_MODE(33, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "m", "decc_err", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad3", V_88F6810_PLUS)), + MPP_MODE(34, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad1", V_88F6810_PLUS)), + MPP_MODE(35, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ref", "clk_out1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "a1", V_88F6810_PLUS)), + MPP_MODE(36, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ptp", "trig_gen", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "a0", V_88F6810_PLUS)), + MPP_MODE(37, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ptp", "clk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad8", V_88F6810_PLUS)), + MPP_MODE(38, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ptp", "event_req", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxd1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ref", "clk_out0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad4", V_88F6810_PLUS)), + MPP_MODE(39, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "i2c1", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxd2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "cts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "a2", V_88F6810_PLUS)), + MPP_MODE(40, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "i2c1", "sda", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxd3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "rts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "sd0", "d2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad6", V_88F6810_PLUS)), + MPP_MODE(41, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "rxd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge1", "rxctl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "cts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs3", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "burst/last", V_88F6810_PLUS)), + MPP_MODE(42, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "txd", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "rts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad7", V_88F6810_PLUS)), + MPP_MODE(43, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "clkreq", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "m", "vtt_ctrl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "m", "decc_err", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "dev", "clkout", V_88F6810_PLUS)), + MPP_MODE(44, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "sata2", "prsnt", V_88F6828), + MPP_VAR_FUNCTION(4, "sata3", "prsnt", V_88F6828), + MPP_VAR_FUNCTION(5, "pcie0", "rstout", V_88F6810_PLUS)), + MPP_MODE(45, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ref", "clk_out0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie1", "rstout", V_88F6820_PLUS), + MPP_VAR_FUNCTION(4, "pcie2", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "pcie3", "rstout", V_88F6810_PLUS)), + MPP_MODE(46, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ref", "clk_out1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie1", "rstout", V_88F6820_PLUS), + MPP_VAR_FUNCTION(4, "pcie2", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "pcie3", "rstout", V_88F6810_PLUS)), + MPP_MODE(47, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "sata2", "prsnt", V_88F6828), + MPP_VAR_FUNCTION(4, "spi1", "cs2", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sata3", "prsnt", V_88F6828)), + MPP_MODE(48, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "m", "vtt_ctrl", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "tdm2c", "pclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "mclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d4", V_88F6810_PLUS)), + MPP_MODE(49, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata2", "prsnt", V_88F6828), + MPP_VAR_FUNCTION(2, "sata3", "prsnt", V_88F6828), + MPP_VAR_FUNCTION(3, "tdm2c", "fsync", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "lrclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d5", V_88F6810_PLUS)), + MPP_MODE(50, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "pcie1", "rstout", V_88F6820_PLUS), + MPP_VAR_FUNCTION(3, "tdm2c", "drx", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "extclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "cmd", V_88F6810_PLUS)), + MPP_MODE(51, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "tdm2c", "dtx", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "sdo", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "m", "decc_err", V_88F6810_PLUS)), + MPP_MODE(52, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "pcie1", "rstout", V_88F6820_PLUS), + MPP_VAR_FUNCTION(3, "tdm2c", "intn", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "sdi", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d6", V_88F6810_PLUS)), + MPP_MODE(53, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "tdm2c", "rstn", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "audio", "bclk", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d7", V_88F6810_PLUS)), + MPP_MODE(54, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "sata1", "prsnt", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "pcie1", "rstout", V_88F6820_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d3", V_88F6810_PLUS)), + MPP_MODE(55, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "cts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge", "mdio", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie1", "clkreq", V_88F6820_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs1", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d0", V_88F6810_PLUS)), + MPP_MODE(56, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "rts", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "ge", "mdc", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "m", "decc_err", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "mosi", V_88F6810_PLUS)), + MPP_MODE(57, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "clk", V_88F6810_PLUS)), + MPP_MODE(58, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "pcie1", "clkreq", V_88F6820_PLUS), + MPP_VAR_FUNCTION(2, "i2c1", "sck", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie2", "clkreq", V_88F6810_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "miso", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d1", V_88F6810_PLUS)), + MPP_MODE(59, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6810_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "rstout", V_88F6810_PLUS), + MPP_VAR_FUNCTION(2, "i2c1", "sda", V_88F6810_PLUS), + MPP_VAR_FUNCTION(3, "pcie1", "rstout", V_88F6820_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs0", V_88F6810_PLUS), + MPP_VAR_FUNCTION(5, "sd0", "d2", V_88F6810_PLUS)), +}; + +static struct mvebu_pinctrl_soc_info armada_38x_pinctrl_info; + +static struct of_device_id armada_38x_pinctrl_of_match[] = { + { + .compatible = "marvell,mv88f6810-pinctrl", + .data = (void *) V_88F6810, + }, + { + .compatible = "marvell,mv88f6820-pinctrl", + .data = (void *) V_88F6820, + }, + { + .compatible = "marvell,mv88f6828-pinctrl", + .data = (void *) V_88F6828, + }, + { }, +}; + +static struct mvebu_mpp_ctrl armada_38x_mpp_controls[] = { + MPP_FUNC_CTRL(0, 59, NULL, armada_38x_mpp_ctrl), +}; + +static struct pinctrl_gpio_range armada_38x_mpp_gpio_ranges[] = { + MPP_GPIO_RANGE(0, 0, 0, 32), + MPP_GPIO_RANGE(1, 32, 32, 27), +}; + +static int armada_38x_pinctrl_probe(struct platform_device *pdev) +{ + struct mvebu_pinctrl_soc_info *soc = &armada_38x_pinctrl_info; + const struct of_device_id *match = + of_match_device(armada_38x_pinctrl_of_match, &pdev->dev); + struct resource *res; + + if (!match) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mpp_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mpp_base)) + return PTR_ERR(mpp_base); + + soc->variant = (unsigned) match->data & 0xff; + soc->controls = armada_38x_mpp_controls; + soc->ncontrols = ARRAY_SIZE(armada_38x_mpp_controls); + soc->gpioranges = armada_38x_mpp_gpio_ranges; + soc->ngpioranges = ARRAY_SIZE(armada_38x_mpp_gpio_ranges); + soc->modes = armada_38x_mpp_modes; + soc->nmodes = armada_38x_mpp_controls[0].npins; + + pdev->dev.platform_data = soc; + + return mvebu_pinctrl_probe(pdev); +} + +static int armada_38x_pinctrl_remove(struct platform_device *pdev) +{ + return mvebu_pinctrl_remove(pdev); +} + +static struct platform_driver armada_38x_pinctrl_driver = { + .driver = { + .name = "armada-38x-pinctrl", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(armada_38x_pinctrl_of_match), + }, + .probe = armada_38x_pinctrl_probe, + .remove = armada_38x_pinctrl_remove, +}; + +module_platform_driver(armada_38x_pinctrl_driver); + +MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); +MODULE_DESCRIPTION("Marvell Armada 38x pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-xp.c b/drivers/pinctrl/mvebu/pinctrl-armada-xp.c index 843a51f9d129..de311129f7a0 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-xp.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-xp.c @@ -33,6 +33,18 @@ #include "pinctrl-mvebu.h" +static void __iomem *mpp_base; + +static int armada_xp_mpp_ctrl_get(unsigned pid, unsigned long *config) +{ + return default_mpp_ctrl_get(mpp_base, pid, config); +} + +static int armada_xp_mpp_ctrl_set(unsigned pid, unsigned long config) +{ + return default_mpp_ctrl_set(mpp_base, pid, config); +} + enum armada_xp_variant { V_MV78230 = BIT(0), V_MV78260 = BIT(1), @@ -366,7 +378,7 @@ static struct of_device_id armada_xp_pinctrl_of_match[] = { }; static struct mvebu_mpp_ctrl mv78230_mpp_controls[] = { - MPP_REG_CTRL(0, 48), + MPP_FUNC_CTRL(0, 48, NULL, armada_xp_mpp_ctrl), }; static struct pinctrl_gpio_range mv78230_mpp_gpio_ranges[] = { @@ -375,7 +387,7 @@ static struct pinctrl_gpio_range mv78230_mpp_gpio_ranges[] = { }; static struct mvebu_mpp_ctrl mv78260_mpp_controls[] = { - MPP_REG_CTRL(0, 66), + MPP_FUNC_CTRL(0, 66, NULL, armada_xp_mpp_ctrl), }; static struct pinctrl_gpio_range mv78260_mpp_gpio_ranges[] = { @@ -385,7 +397,7 @@ static struct pinctrl_gpio_range mv78260_mpp_gpio_ranges[] = { }; static struct mvebu_mpp_ctrl mv78460_mpp_controls[] = { - MPP_REG_CTRL(0, 66), + MPP_FUNC_CTRL(0, 66, NULL, armada_xp_mpp_ctrl), }; static struct pinctrl_gpio_range mv78460_mpp_gpio_ranges[] = { @@ -399,10 +411,16 @@ static int armada_xp_pinctrl_probe(struct platform_device *pdev) struct mvebu_pinctrl_soc_info *soc = &armada_xp_pinctrl_info; const struct of_device_id *match = of_match_device(armada_xp_pinctrl_of_match, &pdev->dev); + struct resource *res; if (!match) return -ENODEV; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mpp_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mpp_base)) + return PTR_ERR(mpp_base); + soc->variant = (unsigned) match->data & 0xff; switch (soc->variant) { diff --git a/drivers/pinctrl/mvebu/pinctrl-dove.c b/drivers/pinctrl/mvebu/pinctrl-dove.c index 47268393af34..3b022178a566 100644 --- a/drivers/pinctrl/mvebu/pinctrl-dove.c +++ b/drivers/pinctrl/mvebu/pinctrl-dove.c @@ -18,107 +18,122 @@ #include <linux/clk.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/mfd/syscon.h> #include <linux/pinctrl/pinctrl.h> +#include <linux/regmap.h> #include "pinctrl-mvebu.h" -#define DOVE_SB_REGS_VIRT_BASE IOMEM(0xfde00000) -#define DOVE_MPP_VIRT_BASE (DOVE_SB_REGS_VIRT_BASE + 0xd0200) -#define DOVE_PMU_MPP_GENERAL_CTRL (DOVE_MPP_VIRT_BASE + 0x10) -#define DOVE_AU0_AC97_SEL BIT(16) -#define DOVE_PMU_SIGNAL_SELECT_0 (DOVE_SB_REGS_VIRT_BASE + 0xd802C) -#define DOVE_PMU_SIGNAL_SELECT_1 (DOVE_SB_REGS_VIRT_BASE + 0xd8030) -#define DOVE_GLOBAL_CONFIG_1 (DOVE_SB_REGS_VIRT_BASE + 0xe802C) -#define DOVE_GLOBAL_CONFIG_1 (DOVE_SB_REGS_VIRT_BASE + 0xe802C) -#define DOVE_TWSI_ENABLE_OPTION1 BIT(7) -#define DOVE_GLOBAL_CONFIG_2 (DOVE_SB_REGS_VIRT_BASE + 0xe8030) -#define DOVE_TWSI_ENABLE_OPTION2 BIT(20) -#define DOVE_TWSI_ENABLE_OPTION3 BIT(21) -#define DOVE_TWSI_OPTION3_GPIO BIT(22) -#define DOVE_SSP_CTRL_STATUS_1 (DOVE_SB_REGS_VIRT_BASE + 0xe8034) -#define DOVE_SSP_ON_AU1 BIT(0) -#define DOVE_MPP_GENERAL_VIRT_BASE (DOVE_SB_REGS_VIRT_BASE + 0xe803c) -#define DOVE_AU1_SPDIFO_GPIO_EN BIT(1) -#define DOVE_NAND_GPIO_EN BIT(0) -#define DOVE_GPIO_LO_VIRT_BASE (DOVE_SB_REGS_VIRT_BASE + 0xd0400) -#define DOVE_MPP_CTRL4_VIRT_BASE (DOVE_GPIO_LO_VIRT_BASE + 0x40) -#define DOVE_SPI_GPIO_SEL BIT(5) -#define DOVE_UART1_GPIO_SEL BIT(4) -#define DOVE_AU1_GPIO_SEL BIT(3) -#define DOVE_CAM_GPIO_SEL BIT(2) -#define DOVE_SD1_GPIO_SEL BIT(1) -#define DOVE_SD0_GPIO_SEL BIT(0) - -#define MPPS_PER_REG 8 -#define MPP_BITS 4 -#define MPP_MASK 0xf +/* Internal registers can be configured at any 1 MiB aligned address */ +#define INT_REGS_MASK ~(SZ_1M - 1) +#define MPP4_REGS_OFFS 0xd0440 +#define PMU_REGS_OFFS 0xd802c +#define GC_REGS_OFFS 0xe802c + +/* MPP Base registers */ +#define PMU_MPP_GENERAL_CTRL 0x10 +#define AU0_AC97_SEL BIT(16) + +/* MPP Control 4 register */ +#define SPI_GPIO_SEL BIT(5) +#define UART1_GPIO_SEL BIT(4) +#define AU1_GPIO_SEL BIT(3) +#define CAM_GPIO_SEL BIT(2) +#define SD1_GPIO_SEL BIT(1) +#define SD0_GPIO_SEL BIT(0) + +/* PMU Signal Select registers */ +#define PMU_SIGNAL_SELECT_0 0x00 +#define PMU_SIGNAL_SELECT_1 0x04 + +/* Global Config regmap registers */ +#define GLOBAL_CONFIG_1 0x00 +#define TWSI_ENABLE_OPTION1 BIT(7) +#define GLOBAL_CONFIG_2 0x04 +#define TWSI_ENABLE_OPTION2 BIT(20) +#define TWSI_ENABLE_OPTION3 BIT(21) +#define TWSI_OPTION3_GPIO BIT(22) +#define SSP_CTRL_STATUS_1 0x08 +#define SSP_ON_AU1 BIT(0) +#define MPP_GENERAL_CONFIG 0x10 +#define AU1_SPDIFO_GPIO_EN BIT(1) +#define NAND_GPIO_EN BIT(0) #define CONFIG_PMU BIT(4) -static int dove_pmu_mpp_ctrl_get(struct mvebu_mpp_ctrl *ctrl, - unsigned long *config) +static void __iomem *mpp_base; +static void __iomem *mpp4_base; +static void __iomem *pmu_base; +static struct regmap *gconfmap; + +static int dove_mpp_ctrl_get(unsigned pid, unsigned long *config) +{ + return default_mpp_ctrl_get(mpp_base, pid, config); +} + +static int dove_mpp_ctrl_set(unsigned pid, unsigned long config) { - unsigned off = (ctrl->pid / MPPS_PER_REG) * MPP_BITS; - unsigned shift = (ctrl->pid % MPPS_PER_REG) * MPP_BITS; - unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL); + return default_mpp_ctrl_set(mpp_base, pid, config); +} + +static int dove_pmu_mpp_ctrl_get(unsigned pid, unsigned long *config) +{ + unsigned off = (pid / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + unsigned shift = (pid % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + unsigned long pmu = readl(mpp_base + PMU_MPP_GENERAL_CTRL); unsigned long func; - if (pmu & (1 << ctrl->pid)) { - func = readl(DOVE_PMU_SIGNAL_SELECT_0 + off); - *config = (func >> shift) & MPP_MASK; - *config |= CONFIG_PMU; - } else { - func = readl(DOVE_MPP_VIRT_BASE + off); - *config = (func >> shift) & MPP_MASK; - } + if ((pmu & BIT(pid)) == 0) + return default_mpp_ctrl_get(mpp_base, pid, config); + + func = readl(pmu_base + PMU_SIGNAL_SELECT_0 + off); + *config = (func >> shift) & MVEBU_MPP_MASK; + *config |= CONFIG_PMU; + return 0; } -static int dove_pmu_mpp_ctrl_set(struct mvebu_mpp_ctrl *ctrl, - unsigned long config) +static int dove_pmu_mpp_ctrl_set(unsigned pid, unsigned long config) { - unsigned off = (ctrl->pid / MPPS_PER_REG) * MPP_BITS; - unsigned shift = (ctrl->pid % MPPS_PER_REG) * MPP_BITS; - unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL); + unsigned off = (pid / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + unsigned shift = (pid % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + unsigned long pmu = readl(mpp_base + PMU_MPP_GENERAL_CTRL); unsigned long func; - if (config & CONFIG_PMU) { - writel(pmu | (1 << ctrl->pid), DOVE_PMU_MPP_GENERAL_CTRL); - func = readl(DOVE_PMU_SIGNAL_SELECT_0 + off); - func &= ~(MPP_MASK << shift); - func |= (config & MPP_MASK) << shift; - writel(func, DOVE_PMU_SIGNAL_SELECT_0 + off); - } else { - writel(pmu & ~(1 << ctrl->pid), DOVE_PMU_MPP_GENERAL_CTRL); - func = readl(DOVE_MPP_VIRT_BASE + off); - func &= ~(MPP_MASK << shift); - func |= (config & MPP_MASK) << shift; - writel(func, DOVE_MPP_VIRT_BASE + off); + if ((config & CONFIG_PMU) == 0) { + writel(pmu & ~BIT(pid), mpp_base + PMU_MPP_GENERAL_CTRL); + return default_mpp_ctrl_set(mpp_base, pid, config); } + + writel(pmu | BIT(pid), mpp_base + PMU_MPP_GENERAL_CTRL); + func = readl(pmu_base + PMU_SIGNAL_SELECT_0 + off); + func &= ~(MVEBU_MPP_MASK << shift); + func |= (config & MVEBU_MPP_MASK) << shift; + writel(func, pmu_base + PMU_SIGNAL_SELECT_0 + off); + return 0; } -static int dove_mpp4_ctrl_get(struct mvebu_mpp_ctrl *ctrl, - unsigned long *config) +static int dove_mpp4_ctrl_get(unsigned pid, unsigned long *config) { - unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE); + unsigned long mpp4 = readl(mpp4_base); unsigned long mask; - switch (ctrl->pid) { + switch (pid) { case 24: /* mpp_camera */ - mask = DOVE_CAM_GPIO_SEL; + mask = CAM_GPIO_SEL; break; case 40: /* mpp_sdio0 */ - mask = DOVE_SD0_GPIO_SEL; + mask = SD0_GPIO_SEL; break; case 46: /* mpp_sdio1 */ - mask = DOVE_SD1_GPIO_SEL; + mask = SD1_GPIO_SEL; break; case 58: /* mpp_spi0 */ - mask = DOVE_SPI_GPIO_SEL; + mask = SPI_GPIO_SEL; break; case 62: /* mpp_uart1 */ - mask = DOVE_UART1_GPIO_SEL; + mask = UART1_GPIO_SEL; break; default: return -EINVAL; @@ -129,27 +144,26 @@ static int dove_mpp4_ctrl_get(struct mvebu_mpp_ctrl *ctrl, return 0; } -static int dove_mpp4_ctrl_set(struct mvebu_mpp_ctrl *ctrl, - unsigned long config) +static int dove_mpp4_ctrl_set(unsigned pid, unsigned long config) { - unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE); + unsigned long mpp4 = readl(mpp4_base); unsigned long mask; - switch (ctrl->pid) { + switch (pid) { case 24: /* mpp_camera */ - mask = DOVE_CAM_GPIO_SEL; + mask = CAM_GPIO_SEL; break; case 40: /* mpp_sdio0 */ - mask = DOVE_SD0_GPIO_SEL; + mask = SD0_GPIO_SEL; break; case 46: /* mpp_sdio1 */ - mask = DOVE_SD1_GPIO_SEL; + mask = SD1_GPIO_SEL; break; case 58: /* mpp_spi0 */ - mask = DOVE_SPI_GPIO_SEL; + mask = SPI_GPIO_SEL; break; case 62: /* mpp_uart1 */ - mask = DOVE_UART1_GPIO_SEL; + mask = UART1_GPIO_SEL; break; default: return -EINVAL; @@ -159,74 +173,69 @@ static int dove_mpp4_ctrl_set(struct mvebu_mpp_ctrl *ctrl, if (config) mpp4 |= mask; - writel(mpp4, DOVE_MPP_CTRL4_VIRT_BASE); + writel(mpp4, mpp4_base); return 0; } -static int dove_nand_ctrl_get(struct mvebu_mpp_ctrl *ctrl, - unsigned long *config) +static int dove_nand_ctrl_get(unsigned pid, unsigned long *config) { - unsigned long gmpp = readl(DOVE_MPP_GENERAL_VIRT_BASE); + unsigned int gmpp; - *config = ((gmpp & DOVE_NAND_GPIO_EN) != 0); + regmap_read(gconfmap, MPP_GENERAL_CONFIG, &gmpp); + *config = ((gmpp & NAND_GPIO_EN) != 0); return 0; } -static int dove_nand_ctrl_set(struct mvebu_mpp_ctrl *ctrl, - unsigned long config) +static int dove_nand_ctrl_set(unsigned pid, unsigned long config) { - unsigned long gmpp = readl(DOVE_MPP_GENERAL_VIRT_BASE); - - gmpp &= ~DOVE_NAND_GPIO_EN; - if (config) - gmpp |= DOVE_NAND_GPIO_EN; - - writel(gmpp, DOVE_MPP_GENERAL_VIRT_BASE); - + regmap_update_bits(gconfmap, MPP_GENERAL_CONFIG, + NAND_GPIO_EN, + (config) ? NAND_GPIO_EN : 0); return 0; } -static int dove_audio0_ctrl_get(struct mvebu_mpp_ctrl *ctrl, - unsigned long *config) +static int dove_audio0_ctrl_get(unsigned pid, unsigned long *config) { - unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL); + unsigned long pmu = readl(mpp_base + PMU_MPP_GENERAL_CTRL); - *config = ((pmu & DOVE_AU0_AC97_SEL) != 0); + *config = ((pmu & AU0_AC97_SEL) != 0); return 0; } -static int dove_audio0_ctrl_set(struct mvebu_mpp_ctrl *ctrl, - unsigned long config) +static int dove_audio0_ctrl_set(unsigned pid, unsigned long config) { - unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL); + unsigned long pmu = readl(mpp_base + PMU_MPP_GENERAL_CTRL); - pmu &= ~DOVE_AU0_AC97_SEL; + pmu &= ~AU0_AC97_SEL; if (config) - pmu |= DOVE_AU0_AC97_SEL; - writel(pmu, DOVE_PMU_MPP_GENERAL_CTRL); + pmu |= AU0_AC97_SEL; + writel(pmu, mpp_base + PMU_MPP_GENERAL_CTRL); return 0; } -static int dove_audio1_ctrl_get(struct mvebu_mpp_ctrl *ctrl, - unsigned long *config) +static int dove_audio1_ctrl_get(unsigned pid, unsigned long *config) { - unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE); - unsigned long sspc1 = readl(DOVE_SSP_CTRL_STATUS_1); - unsigned long gmpp = readl(DOVE_MPP_GENERAL_VIRT_BASE); - unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2); + unsigned int mpp4 = readl(mpp4_base); + unsigned int sspc1; + unsigned int gmpp; + unsigned int gcfg2; + + regmap_read(gconfmap, SSP_CTRL_STATUS_1, &sspc1); + regmap_read(gconfmap, MPP_GENERAL_CONFIG, &gmpp); + regmap_read(gconfmap, GLOBAL_CONFIG_2, &gcfg2); *config = 0; - if (mpp4 & DOVE_AU1_GPIO_SEL) + if (mpp4 & AU1_GPIO_SEL) *config |= BIT(3); - if (sspc1 & DOVE_SSP_ON_AU1) + if (sspc1 & SSP_ON_AU1) *config |= BIT(2); - if (gmpp & DOVE_AU1_SPDIFO_GPIO_EN) + if (gmpp & AU1_SPDIFO_GPIO_EN) *config |= BIT(1); - if (gcfg2 & DOVE_TWSI_OPTION3_GPIO) + if (gcfg2 & TWSI_OPTION3_GPIO) *config |= BIT(0); /* SSP/TWSI only if I2S1 not set*/ @@ -238,35 +247,24 @@ static int dove_audio1_ctrl_get(struct mvebu_mpp_ctrl *ctrl, return 0; } -static int dove_audio1_ctrl_set(struct mvebu_mpp_ctrl *ctrl, - unsigned long config) +static int dove_audio1_ctrl_set(unsigned pid, unsigned long config) { - unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE); - unsigned long sspc1 = readl(DOVE_SSP_CTRL_STATUS_1); - unsigned long gmpp = readl(DOVE_MPP_GENERAL_VIRT_BASE); - unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2); + unsigned int mpp4 = readl(mpp4_base); - /* - * clear all audio1 related bits before configure - */ - gcfg2 &= ~DOVE_TWSI_OPTION3_GPIO; - gmpp &= ~DOVE_AU1_SPDIFO_GPIO_EN; - sspc1 &= ~DOVE_SSP_ON_AU1; - mpp4 &= ~DOVE_AU1_GPIO_SEL; - - if (config & BIT(0)) - gcfg2 |= DOVE_TWSI_OPTION3_GPIO; - if (config & BIT(1)) - gmpp |= DOVE_AU1_SPDIFO_GPIO_EN; - if (config & BIT(2)) - sspc1 |= DOVE_SSP_ON_AU1; + mpp4 &= ~AU1_GPIO_SEL; if (config & BIT(3)) - mpp4 |= DOVE_AU1_GPIO_SEL; - - writel(mpp4, DOVE_MPP_CTRL4_VIRT_BASE); - writel(sspc1, DOVE_SSP_CTRL_STATUS_1); - writel(gmpp, DOVE_MPP_GENERAL_VIRT_BASE); - writel(gcfg2, DOVE_GLOBAL_CONFIG_2); + mpp4 |= AU1_GPIO_SEL; + writel(mpp4, mpp4_base); + + regmap_update_bits(gconfmap, SSP_CTRL_STATUS_1, + SSP_ON_AU1, + (config & BIT(2)) ? SSP_ON_AU1 : 0); + regmap_update_bits(gconfmap, MPP_GENERAL_CONFIG, + AU1_SPDIFO_GPIO_EN, + (config & BIT(1)) ? AU1_SPDIFO_GPIO_EN : 0); + regmap_update_bits(gconfmap, GLOBAL_CONFIG_2, + TWSI_OPTION3_GPIO, + (config & BIT(0)) ? TWSI_OPTION3_GPIO : 0); return 0; } @@ -276,11 +274,11 @@ static int dove_audio1_ctrl_set(struct mvebu_mpp_ctrl *ctrl, * break other functions. If you require all mpps as gpio * enforce gpio setting by pinctrl mapping. */ -static int dove_audio1_ctrl_gpio_req(struct mvebu_mpp_ctrl *ctrl, u8 pid) +static int dove_audio1_ctrl_gpio_req(unsigned pid) { unsigned long config; - dove_audio1_ctrl_get(ctrl, &config); + dove_audio1_ctrl_get(pid, &config); switch (config) { case 0x02: /* i2s1 : gpio[56:57] */ @@ -303,76 +301,62 @@ static int dove_audio1_ctrl_gpio_req(struct mvebu_mpp_ctrl *ctrl, u8 pid) } /* mpp[52:57] has gpio pins capable of in and out */ -static int dove_audio1_ctrl_gpio_dir(struct mvebu_mpp_ctrl *ctrl, u8 pid, - bool input) +static int dove_audio1_ctrl_gpio_dir(unsigned pid, bool input) { if (pid < 52 || pid > 57) return -ENOTSUPP; return 0; } -static int dove_twsi_ctrl_get(struct mvebu_mpp_ctrl *ctrl, - unsigned long *config) +static int dove_twsi_ctrl_get(unsigned pid, unsigned long *config) { - unsigned long gcfg1 = readl(DOVE_GLOBAL_CONFIG_1); - unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2); + unsigned int gcfg1; + unsigned int gcfg2; + + regmap_read(gconfmap, GLOBAL_CONFIG_1, &gcfg1); + regmap_read(gconfmap, GLOBAL_CONFIG_2, &gcfg2); *config = 0; - if (gcfg1 & DOVE_TWSI_ENABLE_OPTION1) + if (gcfg1 & TWSI_ENABLE_OPTION1) *config = 1; - else if (gcfg2 & DOVE_TWSI_ENABLE_OPTION2) + else if (gcfg2 & TWSI_ENABLE_OPTION2) *config = 2; - else if (gcfg2 & DOVE_TWSI_ENABLE_OPTION3) + else if (gcfg2 & TWSI_ENABLE_OPTION3) *config = 3; return 0; } -static int dove_twsi_ctrl_set(struct mvebu_mpp_ctrl *ctrl, - unsigned long config) +static int dove_twsi_ctrl_set(unsigned pid, unsigned long config) { - unsigned long gcfg1 = readl(DOVE_GLOBAL_CONFIG_1); - unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2); - - gcfg1 &= ~DOVE_TWSI_ENABLE_OPTION1; - gcfg2 &= ~(DOVE_TWSI_ENABLE_OPTION2 | DOVE_TWSI_ENABLE_OPTION3); + unsigned int gcfg1 = 0; + unsigned int gcfg2 = 0; switch (config) { case 1: - gcfg1 |= DOVE_TWSI_ENABLE_OPTION1; + gcfg1 = TWSI_ENABLE_OPTION1; break; case 2: - gcfg2 |= DOVE_TWSI_ENABLE_OPTION2; + gcfg2 = TWSI_ENABLE_OPTION2; break; case 3: - gcfg2 |= DOVE_TWSI_ENABLE_OPTION3; + gcfg2 = TWSI_ENABLE_OPTION3; break; } - writel(gcfg1, DOVE_GLOBAL_CONFIG_1); - writel(gcfg2, DOVE_GLOBAL_CONFIG_2); + regmap_update_bits(gconfmap, GLOBAL_CONFIG_1, + TWSI_ENABLE_OPTION1, + gcfg1); + regmap_update_bits(gconfmap, GLOBAL_CONFIG_2, + TWSI_ENABLE_OPTION2 | TWSI_ENABLE_OPTION3, + gcfg2); return 0; } static struct mvebu_mpp_ctrl dove_mpp_controls[] = { - MPP_FUNC_CTRL(0, 0, "mpp0", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(1, 1, "mpp1", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(2, 2, "mpp2", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(3, 3, "mpp3", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(4, 4, "mpp4", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(5, 5, "mpp5", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(6, 6, "mpp6", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(7, 7, "mpp7", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(8, 8, "mpp8", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(9, 9, "mpp9", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(10, 10, "mpp10", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(11, 11, "mpp11", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(12, 12, "mpp12", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(13, 13, "mpp13", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(14, 14, "mpp14", dove_pmu_mpp_ctrl), - MPP_FUNC_CTRL(15, 15, "mpp15", dove_pmu_mpp_ctrl), - MPP_REG_CTRL(16, 23), + MPP_FUNC_CTRL(0, 15, NULL, dove_pmu_mpp_ctrl), + MPP_FUNC_CTRL(16, 23, NULL, dove_mpp_ctrl), MPP_FUNC_CTRL(24, 39, "mpp_camera", dove_mpp4_ctrl), MPP_FUNC_CTRL(40, 45, "mpp_sdio0", dove_mpp4_ctrl), MPP_FUNC_CTRL(46, 51, "mpp_sdio1", dove_mpp4_ctrl), @@ -772,8 +756,17 @@ static struct of_device_id dove_pinctrl_of_match[] = { { } }; +static struct regmap_config gc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 5, +}; + static int dove_pinctrl_probe(struct platform_device *pdev) { + struct resource *res, *mpp_res; + struct resource fb_res; const struct of_device_id *match = of_match_device(dove_pinctrl_of_match, &pdev->dev); pdev->dev.platform_data = (void *)match->data; @@ -789,6 +782,59 @@ static int dove_pinctrl_probe(struct platform_device *pdev) } clk_prepare_enable(clk); + mpp_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mpp_base = devm_ioremap_resource(&pdev->dev, mpp_res); + if (IS_ERR(mpp_base)) + return PTR_ERR(mpp_base); + + /* prepare fallback resource */ + memcpy(&fb_res, mpp_res, sizeof(struct resource)); + fb_res.start = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_warn(&pdev->dev, "falling back to hardcoded MPP4 resource\n"); + adjust_resource(&fb_res, + (mpp_res->start & INT_REGS_MASK) + MPP4_REGS_OFFS, 0x4); + res = &fb_res; + } + + mpp4_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mpp4_base)) + return PTR_ERR(mpp4_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) { + dev_warn(&pdev->dev, "falling back to hardcoded PMU resource\n"); + adjust_resource(&fb_res, + (mpp_res->start & INT_REGS_MASK) + PMU_REGS_OFFS, 0x8); + res = &fb_res; + } + + pmu_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pmu_base)) + return PTR_ERR(pmu_base); + + gconfmap = syscon_regmap_lookup_by_compatible("marvell,dove-global-config"); + if (IS_ERR(gconfmap)) { + void __iomem *gc_base; + + dev_warn(&pdev->dev, "falling back to hardcoded global registers\n"); + adjust_resource(&fb_res, + (mpp_res->start & INT_REGS_MASK) + GC_REGS_OFFS, 0x14); + gc_base = devm_ioremap_resource(&pdev->dev, &fb_res); + if (IS_ERR(gc_base)) + return PTR_ERR(gc_base); + gconfmap = devm_regmap_init_mmio(&pdev->dev, + gc_base, &gc_regmap_config); + if (IS_ERR(gconfmap)) + return PTR_ERR(gconfmap); + } + + /* Warn on any missing DT resource */ + if (fb_res.start) + dev_warn(&pdev->dev, FW_BUG "Missing pinctrl regs in DTB. Please update your firmware.\n"); + return mvebu_pinctrl_probe(pdev); } diff --git a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c index 6b504b5935a5..0d0211a1a0b0 100644 --- a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c +++ b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c @@ -21,6 +21,18 @@ #include "pinctrl-mvebu.h" +static void __iomem *mpp_base; + +static int kirkwood_mpp_ctrl_get(unsigned pid, unsigned long *config) +{ + return default_mpp_ctrl_get(mpp_base, pid, config); +} + +static int kirkwood_mpp_ctrl_set(unsigned pid, unsigned long config) +{ + return default_mpp_ctrl_set(mpp_base, pid, config); +} + #define V(f6180, f6190, f6192, f6281, f6282, dx4122) \ ((f6180 << 0) | (f6190 << 1) | (f6192 << 2) | \ (f6281 << 3) | (f6282 << 4) | (dx4122 << 5)) @@ -359,7 +371,7 @@ static struct mvebu_mpp_mode mv88f6xxx_mpp_modes[] = { }; static struct mvebu_mpp_ctrl mv88f6180_mpp_controls[] = { - MPP_REG_CTRL(0, 29), + MPP_FUNC_CTRL(0, 29, NULL, kirkwood_mpp_ctrl), }; static struct pinctrl_gpio_range mv88f6180_gpio_ranges[] = { @@ -367,7 +379,7 @@ static struct pinctrl_gpio_range mv88f6180_gpio_ranges[] = { }; static struct mvebu_mpp_ctrl mv88f619x_mpp_controls[] = { - MPP_REG_CTRL(0, 35), + MPP_FUNC_CTRL(0, 35, NULL, kirkwood_mpp_ctrl), }; static struct pinctrl_gpio_range mv88f619x_gpio_ranges[] = { @@ -376,7 +388,7 @@ static struct pinctrl_gpio_range mv88f619x_gpio_ranges[] = { }; static struct mvebu_mpp_ctrl mv88f628x_mpp_controls[] = { - MPP_REG_CTRL(0, 49), + MPP_FUNC_CTRL(0, 49, NULL, kirkwood_mpp_ctrl), }; static struct pinctrl_gpio_range mv88f628x_gpio_ranges[] = { @@ -456,9 +468,16 @@ static struct of_device_id kirkwood_pinctrl_of_match[] = { static int kirkwood_pinctrl_probe(struct platform_device *pdev) { + struct resource *res; const struct of_device_id *match = of_match_device(kirkwood_pinctrl_of_match, &pdev->dev); pdev->dev.platform_data = (void *)match->data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mpp_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mpp_base)) + return PTR_ERR(mpp_base); + return mvebu_pinctrl_probe(pdev); } diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.c b/drivers/pinctrl/mvebu/pinctrl-mvebu.c index 0fd1ad31fbf9..9908374f8f92 100644 --- a/drivers/pinctrl/mvebu/pinctrl-mvebu.c +++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.c @@ -50,7 +50,6 @@ struct mvebu_pinctrl { struct device *dev; struct pinctrl_dev *pctldev; struct pinctrl_desc desc; - void __iomem *base; struct mvebu_pinctrl_group *groups; unsigned num_groups; struct mvebu_pinctrl_function *functions; @@ -138,43 +137,6 @@ static struct mvebu_pinctrl_function *mvebu_pinctrl_find_function_by_name( return NULL; } -/* - * Common mpp pin configuration registers on MVEBU are - * registers of eight 4-bit values for each mpp setting. - * Register offset and bit mask are calculated accordingly below. - */ -static int mvebu_common_mpp_get(struct mvebu_pinctrl *pctl, - struct mvebu_pinctrl_group *grp, - unsigned long *config) -{ - unsigned pin = grp->gid; - unsigned off = (pin / MPPS_PER_REG) * MPP_BITS; - unsigned shift = (pin % MPPS_PER_REG) * MPP_BITS; - - *config = readl(pctl->base + off); - *config >>= shift; - *config &= MPP_MASK; - - return 0; -} - -static int mvebu_common_mpp_set(struct mvebu_pinctrl *pctl, - struct mvebu_pinctrl_group *grp, - unsigned long config) -{ - unsigned pin = grp->gid; - unsigned off = (pin / MPPS_PER_REG) * MPP_BITS; - unsigned shift = (pin % MPPS_PER_REG) * MPP_BITS; - unsigned long reg; - - reg = readl(pctl->base + off); - reg &= ~(MPP_MASK << shift); - reg |= (config << shift); - writel(reg, pctl->base + off); - - return 0; -} - static int mvebu_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned gid, unsigned long *config) { @@ -184,10 +146,7 @@ static int mvebu_pinconf_group_get(struct pinctrl_dev *pctldev, if (!grp->ctrl) return -EINVAL; - if (grp->ctrl->mpp_get) - return grp->ctrl->mpp_get(grp->ctrl, config); - - return mvebu_common_mpp_get(pctl, grp, config); + return grp->ctrl->mpp_get(grp->pins[0], config); } static int mvebu_pinconf_group_set(struct pinctrl_dev *pctldev, @@ -202,11 +161,7 @@ static int mvebu_pinconf_group_set(struct pinctrl_dev *pctldev, return -EINVAL; for (i = 0; i < num_configs; i++) { - if (grp->ctrl->mpp_set) - ret = grp->ctrl->mpp_set(grp->ctrl, configs[i]); - else - ret = mvebu_common_mpp_set(pctl, grp, configs[i]); - + ret = grp->ctrl->mpp_set(grp->pins[0], configs[i]); if (ret) return ret; } /* for each config */ @@ -347,7 +302,7 @@ static int mvebu_pinmux_gpio_request_enable(struct pinctrl_dev *pctldev, return -EINVAL; if (grp->ctrl->mpp_gpio_req) - return grp->ctrl->mpp_gpio_req(grp->ctrl, offset); + return grp->ctrl->mpp_gpio_req(offset); setting = mvebu_pinctrl_find_gpio_setting(pctl, grp); if (!setting) @@ -370,7 +325,7 @@ static int mvebu_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, return -EINVAL; if (grp->ctrl->mpp_gpio_dir) - return grp->ctrl->mpp_gpio_dir(grp->ctrl, offset, input); + return grp->ctrl->mpp_gpio_dir(offset, input); setting = mvebu_pinctrl_find_gpio_setting(pctl, grp); if (!setting) @@ -593,11 +548,12 @@ static int mvebu_pinctrl_build_functions(struct platform_device *pdev, int mvebu_pinctrl_probe(struct platform_device *pdev) { struct mvebu_pinctrl_soc_info *soc = dev_get_platdata(&pdev->dev); - struct resource *res; struct mvebu_pinctrl *pctl; - void __iomem *base; struct pinctrl_pin_desc *pdesc; unsigned gid, n, k; + unsigned size, noname = 0; + char *noname_buf; + void *p; int ret; if (!soc || !soc->controls || !soc->modes) { @@ -605,11 +561,6 @@ int mvebu_pinctrl_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - pctl = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pinctrl), GFP_KERNEL); if (!pctl) { @@ -623,7 +574,6 @@ int mvebu_pinctrl_probe(struct platform_device *pdev) pctl->desc.pmxops = &mvebu_pinmux_ops; pctl->desc.confops = &mvebu_pinconf_ops; pctl->variant = soc->variant; - pctl->base = base; pctl->dev = &pdev->dev; platform_set_drvdata(pdev, pctl); @@ -633,33 +583,23 @@ int mvebu_pinctrl_probe(struct platform_device *pdev) pctl->desc.npins = 0; for (n = 0; n < soc->ncontrols; n++) { struct mvebu_mpp_ctrl *ctrl = &soc->controls[n]; - char *names; pctl->desc.npins += ctrl->npins; - /* initial control pins */ + /* initialize control's pins[] array */ for (k = 0; k < ctrl->npins; k++) ctrl->pins[k] = ctrl->pid + k; - /* special soc specific control */ - if (ctrl->mpp_get || ctrl->mpp_set) { - if (!ctrl->name || !ctrl->mpp_get || !ctrl->mpp_set) { - dev_err(&pdev->dev, "wrong soc control info\n"); - return -EINVAL; - } + /* + * We allow to pass controls with NULL name that we treat + * as a range of one-pin groups with generic mvebu register + * controls. + */ + if (!ctrl->name) { + pctl->num_groups += ctrl->npins; + noname += ctrl->npins; + } else { pctl->num_groups += 1; - continue; } - - /* generic mvebu register control */ - names = devm_kzalloc(&pdev->dev, ctrl->npins * 8, GFP_KERNEL); - if (!names) { - dev_err(&pdev->dev, "failed to alloc mpp names\n"); - return -ENOMEM; - } - for (k = 0; k < ctrl->npins; k++) - sprintf(names + 8*k, "mpp%d", ctrl->pid+k); - ctrl->name = names; - pctl->num_groups += ctrl->npins; } pdesc = devm_kzalloc(&pdev->dev, pctl->desc.npins * @@ -673,12 +613,17 @@ int mvebu_pinctrl_probe(struct platform_device *pdev) pdesc[n].number = n; pctl->desc.pins = pdesc; - pctl->groups = devm_kzalloc(&pdev->dev, pctl->num_groups * - sizeof(struct mvebu_pinctrl_group), GFP_KERNEL); - if (!pctl->groups) { - dev_err(&pdev->dev, "failed to alloc pinctrl groups\n"); + /* + * allocate groups and name buffers for unnamed groups. + */ + size = pctl->num_groups * sizeof(*pctl->groups) + noname * 8; + p = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!p) { + dev_err(&pdev->dev, "failed to alloc group data\n"); return -ENOMEM; } + pctl->groups = p; + noname_buf = p + pctl->num_groups * sizeof(*pctl->groups); /* assign mpp controls to groups */ gid = 0; @@ -690,17 +635,26 @@ int mvebu_pinctrl_probe(struct platform_device *pdev) pctl->groups[gid].pins = ctrl->pins; pctl->groups[gid].npins = ctrl->npins; - /* generic mvebu register control maps to a number of groups */ - if (!ctrl->mpp_get && !ctrl->mpp_set) { + /* + * We treat unnamed controls as a range of one-pin groups + * with generic mvebu register controls. Use one group for + * each in this range and assign a default group name. + */ + if (!ctrl->name) { + pctl->groups[gid].name = noname_buf; pctl->groups[gid].npins = 1; + sprintf(noname_buf, "mpp%d", ctrl->pid+0); + noname_buf += 8; for (k = 1; k < ctrl->npins; k++) { gid++; pctl->groups[gid].gid = gid; pctl->groups[gid].ctrl = ctrl; - pctl->groups[gid].name = &ctrl->name[8*k]; + pctl->groups[gid].name = noname_buf; pctl->groups[gid].pins = &ctrl->pins[k]; pctl->groups[gid].npins = 1; + sprintf(noname_buf, "mpp%d", ctrl->pid+k); + noname_buf += 8; } } gid++; diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.h b/drivers/pinctrl/mvebu/pinctrl-mvebu.h index 90bd3beee860..65a98e6f7265 100644 --- a/drivers/pinctrl/mvebu/pinctrl-mvebu.h +++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.h @@ -28,20 +28,19 @@ * between two or more different settings, e.g. assign mpp pin 13 to * uart1 or sata. * - * If optional mpp_get/_set functions are set these are used to get/set - * a specific mode. Otherwise it is assumed that the mpp control is based - * on 4-bit groups in subsequent registers. The optional mpp_gpio_req/_dir - * functions can be used to allow pin settings with varying gpio pins. + * The mpp_get/_set functions are mandatory and are used to get/set a + * specific mode. The optional mpp_gpio_req/_dir functions can be used + * to allow pin settings with varying gpio pins. */ struct mvebu_mpp_ctrl { const char *name; u8 pid; u8 npins; unsigned *pins; - int (*mpp_get)(struct mvebu_mpp_ctrl *ctrl, unsigned long *config); - int (*mpp_set)(struct mvebu_mpp_ctrl *ctrl, unsigned long config); - int (*mpp_gpio_req)(struct mvebu_mpp_ctrl *ctrl, u8 pid); - int (*mpp_gpio_dir)(struct mvebu_mpp_ctrl *ctrl, u8 pid, bool input); + int (*mpp_get)(unsigned pid, unsigned long *config); + int (*mpp_set)(unsigned pid, unsigned long config); + int (*mpp_gpio_req)(unsigned pid); + int (*mpp_gpio_dir)(unsigned pid, bool input); }; /** @@ -114,18 +113,6 @@ struct mvebu_pinctrl_soc_info { int ngpioranges; }; -#define MPP_REG_CTRL(_idl, _idh) \ - { \ - .name = NULL, \ - .pid = _idl, \ - .npins = _idh - _idl + 1, \ - .pins = (unsigned[_idh - _idl + 1]) { }, \ - .mpp_get = NULL, \ - .mpp_set = NULL, \ - .mpp_gpio_req = NULL, \ - .mpp_gpio_dir = NULL, \ - } - #define MPP_FUNC_CTRL(_idl, _idh, _name, _func) \ { \ .name = _name, \ @@ -186,6 +173,34 @@ struct mvebu_pinctrl_soc_info { .npins = _npins, \ } +#define MVEBU_MPPS_PER_REG 8 +#define MVEBU_MPP_BITS 4 +#define MVEBU_MPP_MASK 0xf + +static inline int default_mpp_ctrl_get(void __iomem *base, unsigned int pid, + unsigned long *config) +{ + unsigned off = (pid / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + unsigned shift = (pid % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + + *config = (readl(base + off) >> shift) & MVEBU_MPP_MASK; + + return 0; +} + +static inline int default_mpp_ctrl_set(void __iomem *base, unsigned int pid, + unsigned long config) +{ + unsigned off = (pid / MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + unsigned shift = (pid % MVEBU_MPPS_PER_REG) * MVEBU_MPP_BITS; + unsigned long reg; + + reg = readl(base + off) & ~(MVEBU_MPP_MASK << shift); + writel(reg | (config << shift), base + off); + + return 0; +} + int mvebu_pinctrl_probe(struct platform_device *pdev); int mvebu_pinctrl_remove(struct platform_device *pdev); diff --git a/drivers/pinctrl/pinctrl-adi2-bf54x.c b/drivers/pinctrl/pinctrl-adi2-bf54x.c index ea9d9ab9cda1..008a29e92e56 100644 --- a/drivers/pinctrl/pinctrl-adi2-bf54x.c +++ b/drivers/pinctrl/pinctrl-adi2-bf54x.c @@ -309,39 +309,6 @@ static const unsigned keys_8x8_pins[] = { GPIO_PE4, GPIO_PE5, GPIO_PE6, GPIO_PE7, }; -static const struct adi_pin_group adi_pin_groups[] = { - ADI_PIN_GROUP("uart0grp", uart0_pins), - ADI_PIN_GROUP("uart1grp", uart1_pins), - ADI_PIN_GROUP("uart1ctsrtsgrp", uart1_ctsrts_pins), - ADI_PIN_GROUP("uart2grp", uart2_pins), - ADI_PIN_GROUP("uart3grp", uart3_pins), - ADI_PIN_GROUP("uart3ctsrtsgrp", uart3_ctsrts_pins), - ADI_PIN_GROUP("rsi0grp", rsi0_pins), - ADI_PIN_GROUP("spi0grp", spi0_pins), - ADI_PIN_GROUP("spi1grp", spi1_pins), - ADI_PIN_GROUP("twi0grp", twi0_pins), - ADI_PIN_GROUP("twi1grp", twi1_pins), - ADI_PIN_GROUP("rotarygrp", rotary_pins), - ADI_PIN_GROUP("can0grp", can0_pins), - ADI_PIN_GROUP("can1grp", can1_pins), - ADI_PIN_GROUP("smc0grp", smc0_pins), - ADI_PIN_GROUP("sport0grp", sport0_pins), - ADI_PIN_GROUP("sport1grp", sport1_pins), - ADI_PIN_GROUP("sport2grp", sport2_pins), - ADI_PIN_GROUP("sport3grp", sport3_pins), - ADI_PIN_GROUP("ppi0_8bgrp", ppi0_8b_pins), - ADI_PIN_GROUP("ppi0_16bgrp", ppi0_16b_pins), - ADI_PIN_GROUP("ppi0_24bgrp", ppi0_24b_pins), - ADI_PIN_GROUP("ppi1_8bgrp", ppi1_8b_pins), - ADI_PIN_GROUP("ppi1_16bgrp", ppi1_16b_pins), - ADI_PIN_GROUP("ppi2_8bgrp", ppi2_8b_pins), - ADI_PIN_GROUP("atapigrp", atapi_pins), - ADI_PIN_GROUP("atapialtergrp", atapi_alter_pins), - ADI_PIN_GROUP("nfc0grp", nfc0_pins), - ADI_PIN_GROUP("keys_4x4grp", keys_4x4_pins), - ADI_PIN_GROUP("keys_8x8grp", keys_8x8_pins), -}; - static const unsigned short uart0_mux[] = { P_UART0_TX, P_UART0_RX, 0 @@ -513,6 +480,39 @@ static const unsigned short keys_8x8_mux[] = { 0 }; +static const struct adi_pin_group adi_pin_groups[] = { + ADI_PIN_GROUP("uart0grp", uart0_pins, uart0_mux), + ADI_PIN_GROUP("uart1grp", uart1_pins, uart1_mux), + ADI_PIN_GROUP("uart1ctsrtsgrp", uart1_ctsrts_pins, uart1_ctsrts_mux), + ADI_PIN_GROUP("uart2grp", uart2_pins, uart2_mux), + ADI_PIN_GROUP("uart3grp", uart3_pins, uart3_mux), + ADI_PIN_GROUP("uart3ctsrtsgrp", uart3_ctsrts_pins, uart3_ctsrts_mux), + ADI_PIN_GROUP("rsi0grp", rsi0_pins, rsi0_mux), + ADI_PIN_GROUP("spi0grp", spi0_pins, spi0_mux), + ADI_PIN_GROUP("spi1grp", spi1_pins, spi1_mux), + ADI_PIN_GROUP("twi0grp", twi0_pins, twi0_mux), + ADI_PIN_GROUP("twi1grp", twi1_pins, twi1_mux), + ADI_PIN_GROUP("rotarygrp", rotary_pins, rotary_mux), + ADI_PIN_GROUP("can0grp", can0_pins, can0_mux), + ADI_PIN_GROUP("can1grp", can1_pins, can1_mux), + ADI_PIN_GROUP("smc0grp", smc0_pins, smc0_mux), + ADI_PIN_GROUP("sport0grp", sport0_pins, sport0_mux), + ADI_PIN_GROUP("sport1grp", sport1_pins, sport1_mux), + ADI_PIN_GROUP("sport2grp", sport2_pins, sport2_mux), + ADI_PIN_GROUP("sport3grp", sport3_pins, sport3_mux), + ADI_PIN_GROUP("ppi0_8bgrp", ppi0_8b_pins, ppi0_8b_mux), + ADI_PIN_GROUP("ppi0_16bgrp", ppi0_16b_pins, ppi0_16b_mux), + ADI_PIN_GROUP("ppi0_24bgrp", ppi0_24b_pins, ppi0_24b_mux), + ADI_PIN_GROUP("ppi1_8bgrp", ppi1_8b_pins, ppi1_8b_mux), + ADI_PIN_GROUP("ppi1_16bgrp", ppi1_16b_pins, ppi1_16b_mux), + ADI_PIN_GROUP("ppi2_8bgrp", ppi2_8b_pins, ppi2_8b_mux), + ADI_PIN_GROUP("atapigrp", atapi_pins, atapi_mux), + ADI_PIN_GROUP("atapialtergrp", atapi_alter_pins, atapi_alter_mux), + ADI_PIN_GROUP("nfc0grp", nfc0_pins, nfc0_mux), + ADI_PIN_GROUP("keys_4x4grp", keys_4x4_pins, keys_4x4_mux), + ADI_PIN_GROUP("keys_8x8grp", keys_8x8_pins, keys_8x8_mux), +}; + static const char * const uart0grp[] = { "uart0grp" }; static const char * const uart1grp[] = { "uart1grp" }; static const char * const uart1ctsrtsgrp[] = { "uart1ctsrtsgrp" }; @@ -532,49 +532,45 @@ static const char * const sport0grp[] = { "sport0grp" }; static const char * const sport1grp[] = { "sport1grp" }; static const char * const sport2grp[] = { "sport2grp" }; static const char * const sport3grp[] = { "sport3grp" }; -static const char * const ppi0_8bgrp[] = { "ppi0_8bgrp" }; -static const char * const ppi0_16bgrp[] = { "ppi0_16bgrp" }; -static const char * const ppi0_24bgrp[] = { "ppi0_24bgrp" }; -static const char * const ppi1_8bgrp[] = { "ppi1_8bgrp" }; -static const char * const ppi1_16bgrp[] = { "ppi1_16bgrp" }; -static const char * const ppi2_8bgrp[] = { "ppi2_8bgrp" }; +static const char * const ppi0grp[] = { "ppi0_8bgrp", + "ppi0_16bgrp", + "ppi0_24bgrp" }; +static const char * const ppi1grp[] = { "ppi1_8bgrp", + "ppi1_16bgrp" }; +static const char * const ppi2grp[] = { "ppi2_8bgrp" }; static const char * const atapigrp[] = { "atapigrp" }; static const char * const atapialtergrp[] = { "atapialtergrp" }; static const char * const nfc0grp[] = { "nfc0grp" }; -static const char * const keys_4x4grp[] = { "keys_4x4grp" }; -static const char * const keys_8x8grp[] = { "keys_8x8grp" }; +static const char * const keysgrp[] = { "keys_4x4grp", + "keys_8x8grp" }; static const struct adi_pmx_func adi_pmx_functions[] = { - ADI_PMX_FUNCTION("uart0", uart0grp, uart0_mux), - ADI_PMX_FUNCTION("uart1", uart1grp, uart1_mux), - ADI_PMX_FUNCTION("uart1_ctsrts", uart1ctsrtsgrp, uart1_ctsrts_mux), - ADI_PMX_FUNCTION("uart2", uart2grp, uart2_mux), - ADI_PMX_FUNCTION("uart3", uart3grp, uart3_mux), - ADI_PMX_FUNCTION("uart3_ctsrts", uart3ctsrtsgrp, uart3_ctsrts_mux), - ADI_PMX_FUNCTION("rsi0", rsi0grp, rsi0_mux), - ADI_PMX_FUNCTION("spi0", spi0grp, spi0_mux), - ADI_PMX_FUNCTION("spi1", spi1grp, spi1_mux), - ADI_PMX_FUNCTION("twi0", twi0grp, twi0_mux), - ADI_PMX_FUNCTION("twi1", twi1grp, twi1_mux), - ADI_PMX_FUNCTION("rotary", rotarygrp, rotary_mux), - ADI_PMX_FUNCTION("can0", can0grp, can0_mux), - ADI_PMX_FUNCTION("can1", can1grp, can1_mux), - ADI_PMX_FUNCTION("smc0", smc0grp, smc0_mux), - ADI_PMX_FUNCTION("sport0", sport0grp, sport0_mux), - ADI_PMX_FUNCTION("sport1", sport1grp, sport1_mux), - ADI_PMX_FUNCTION("sport2", sport2grp, sport2_mux), - ADI_PMX_FUNCTION("sport3", sport3grp, sport3_mux), - ADI_PMX_FUNCTION("ppi0_8b", ppi0_8bgrp, ppi0_8b_mux), - ADI_PMX_FUNCTION("ppi0_16b", ppi0_16bgrp, ppi0_16b_mux), - ADI_PMX_FUNCTION("ppi0_24b", ppi0_24bgrp, ppi0_24b_mux), - ADI_PMX_FUNCTION("ppi1_8b", ppi1_8bgrp, ppi1_8b_mux), - ADI_PMX_FUNCTION("ppi1_16b", ppi1_16bgrp, ppi1_16b_mux), - ADI_PMX_FUNCTION("ppi2_8b", ppi2_8bgrp, ppi2_8b_mux), - ADI_PMX_FUNCTION("atapi", atapigrp, atapi_mux), - ADI_PMX_FUNCTION("atapi_alter", atapialtergrp, atapi_alter_mux), - ADI_PMX_FUNCTION("nfc0", nfc0grp, nfc0_mux), - ADI_PMX_FUNCTION("keys_4x4", keys_4x4grp, keys_4x4_mux), - ADI_PMX_FUNCTION("keys_8x8", keys_8x8grp, keys_8x8_mux), + ADI_PMX_FUNCTION("uart0", uart0grp), + ADI_PMX_FUNCTION("uart1", uart1grp), + ADI_PMX_FUNCTION("uart1_ctsrts", uart1ctsrtsgrp), + ADI_PMX_FUNCTION("uart2", uart2grp), + ADI_PMX_FUNCTION("uart3", uart3grp), + ADI_PMX_FUNCTION("uart3_ctsrts", uart3ctsrtsgrp), + ADI_PMX_FUNCTION("rsi0", rsi0grp), + ADI_PMX_FUNCTION("spi0", spi0grp), + ADI_PMX_FUNCTION("spi1", spi1grp), + ADI_PMX_FUNCTION("twi0", twi0grp), + ADI_PMX_FUNCTION("twi1", twi1grp), + ADI_PMX_FUNCTION("rotary", rotarygrp), + ADI_PMX_FUNCTION("can0", can0grp), + ADI_PMX_FUNCTION("can1", can1grp), + ADI_PMX_FUNCTION("smc0", smc0grp), + ADI_PMX_FUNCTION("sport0", sport0grp), + ADI_PMX_FUNCTION("sport1", sport1grp), + ADI_PMX_FUNCTION("sport2", sport2grp), + ADI_PMX_FUNCTION("sport3", sport3grp), + ADI_PMX_FUNCTION("ppi0", ppi0grp), + ADI_PMX_FUNCTION("ppi1", ppi1grp), + ADI_PMX_FUNCTION("ppi2", ppi2grp), + ADI_PMX_FUNCTION("atapi", atapigrp), + ADI_PMX_FUNCTION("atapi_alter", atapialtergrp), + ADI_PMX_FUNCTION("nfc0", nfc0grp), + ADI_PMX_FUNCTION("keys", keysgrp), }; static const struct adi_pinctrl_soc_data adi_bf54x_soc = { diff --git a/drivers/pinctrl/pinctrl-adi2-bf60x.c b/drivers/pinctrl/pinctrl-adi2-bf60x.c index bf57aea2826c..4cb59fe9be70 100644 --- a/drivers/pinctrl/pinctrl-adi2-bf60x.c +++ b/drivers/pinctrl/pinctrl-adi2-bf60x.c @@ -259,37 +259,6 @@ static const unsigned lp3_pins[] = { GPIO_PF12, GPIO_PF13, GPIO_PF14, GPIO_PF15, }; -static const struct adi_pin_group adi_pin_groups[] = { - ADI_PIN_GROUP("uart0grp", uart0_pins), - ADI_PIN_GROUP("uart0ctsrtsgrp", uart0_ctsrts_pins), - ADI_PIN_GROUP("uart1grp", uart1_pins), - ADI_PIN_GROUP("uart1ctsrtsgrp", uart1_ctsrts_pins), - ADI_PIN_GROUP("rsi0grp", rsi0_pins), - ADI_PIN_GROUP("eth0grp", eth0_pins), - ADI_PIN_GROUP("eth1grp", eth1_pins), - ADI_PIN_GROUP("spi0grp", spi0_pins), - ADI_PIN_GROUP("spi1grp", spi1_pins), - ADI_PIN_GROUP("twi0grp", twi0_pins), - ADI_PIN_GROUP("twi1grp", twi1_pins), - ADI_PIN_GROUP("rotarygrp", rotary_pins), - ADI_PIN_GROUP("can0grp", can0_pins), - ADI_PIN_GROUP("smc0grp", smc0_pins), - ADI_PIN_GROUP("sport0grp", sport0_pins), - ADI_PIN_GROUP("sport1grp", sport1_pins), - ADI_PIN_GROUP("sport2grp", sport2_pins), - ADI_PIN_GROUP("ppi0_8bgrp", ppi0_8b_pins), - ADI_PIN_GROUP("ppi0_16bgrp", ppi0_16b_pins), - ADI_PIN_GROUP("ppi0_24bgrp", ppi0_24b_pins), - ADI_PIN_GROUP("ppi1_8bgrp", ppi1_8b_pins), - ADI_PIN_GROUP("ppi1_16bgrp", ppi1_16b_pins), - ADI_PIN_GROUP("ppi2_8bgrp", ppi2_8b_pins), - ADI_PIN_GROUP("ppi2_16bgrp", ppi2_16b_pins), - ADI_PIN_GROUP("lp0grp", lp0_pins), - ADI_PIN_GROUP("lp1grp", lp1_pins), - ADI_PIN_GROUP("lp2grp", lp2_pins), - ADI_PIN_GROUP("lp3grp", lp3_pins), -}; - static const unsigned short uart0_mux[] = { P_UART0_TX, P_UART0_RX, 0 @@ -446,6 +415,37 @@ static const unsigned short lp3_mux[] = { 0 }; +static const struct adi_pin_group adi_pin_groups[] = { + ADI_PIN_GROUP("uart0grp", uart0_pins, uart0_mux), + ADI_PIN_GROUP("uart0ctsrtsgrp", uart0_ctsrts_pins, uart0_ctsrts_mux), + ADI_PIN_GROUP("uart1grp", uart1_pins, uart1_mux), + ADI_PIN_GROUP("uart1ctsrtsgrp", uart1_ctsrts_pins, uart1_ctsrts_mux), + ADI_PIN_GROUP("rsi0grp", rsi0_pins, rsi0_mux), + ADI_PIN_GROUP("eth0grp", eth0_pins, eth0_mux), + ADI_PIN_GROUP("eth1grp", eth1_pins, eth1_mux), + ADI_PIN_GROUP("spi0grp", spi0_pins, spi0_mux), + ADI_PIN_GROUP("spi1grp", spi1_pins, spi1_mux), + ADI_PIN_GROUP("twi0grp", twi0_pins, twi0_mux), + ADI_PIN_GROUP("twi1grp", twi1_pins, twi1_mux), + ADI_PIN_GROUP("rotarygrp", rotary_pins, rotary_mux), + ADI_PIN_GROUP("can0grp", can0_pins, can0_mux), + ADI_PIN_GROUP("smc0grp", smc0_pins, smc0_mux), + ADI_PIN_GROUP("sport0grp", sport0_pins, sport0_mux), + ADI_PIN_GROUP("sport1grp", sport1_pins, sport1_mux), + ADI_PIN_GROUP("sport2grp", sport2_pins, sport2_mux), + ADI_PIN_GROUP("ppi0_8bgrp", ppi0_8b_pins, ppi0_8b_mux), + ADI_PIN_GROUP("ppi0_16bgrp", ppi0_16b_pins, ppi0_16b_mux), + ADI_PIN_GROUP("ppi0_24bgrp", ppi0_24b_pins, ppi0_24b_mux), + ADI_PIN_GROUP("ppi1_8bgrp", ppi1_8b_pins, ppi1_8b_mux), + ADI_PIN_GROUP("ppi1_16bgrp", ppi1_16b_pins, ppi1_16b_mux), + ADI_PIN_GROUP("ppi2_8bgrp", ppi2_8b_pins, ppi2_8b_mux), + ADI_PIN_GROUP("ppi2_16bgrp", ppi2_16b_pins, ppi2_16b_mux), + ADI_PIN_GROUP("lp0grp", lp0_pins, lp0_mux), + ADI_PIN_GROUP("lp1grp", lp1_pins, lp1_mux), + ADI_PIN_GROUP("lp2grp", lp2_pins, lp2_mux), + ADI_PIN_GROUP("lp3grp", lp3_pins, lp3_mux), +}; + static const char * const uart0grp[] = { "uart0grp" }; static const char * const uart0ctsrtsgrp[] = { "uart0ctsrtsgrp" }; static const char * const uart1grp[] = { "uart1grp" }; @@ -463,47 +463,43 @@ static const char * const smc0grp[] = { "smc0grp" }; static const char * const sport0grp[] = { "sport0grp" }; static const char * const sport1grp[] = { "sport1grp" }; static const char * const sport2grp[] = { "sport2grp" }; -static const char * const ppi0_8bgrp[] = { "ppi0_8bgrp" }; -static const char * const ppi0_16bgrp[] = { "ppi0_16bgrp" }; -static const char * const ppi0_24bgrp[] = { "ppi0_24bgrp" }; -static const char * const ppi1_8bgrp[] = { "ppi1_8bgrp" }; -static const char * const ppi1_16bgrp[] = { "ppi1_16bgrp" }; -static const char * const ppi2_8bgrp[] = { "ppi2_8bgrp" }; -static const char * const ppi2_16bgrp[] = { "ppi2_16bgrp" }; +static const char * const ppi0grp[] = { "ppi0_8bgrp", + "ppi0_16bgrp", + "ppi0_24bgrp" }; +static const char * const ppi1grp[] = { "ppi1_8bgrp", + "ppi1_16bgrp" }; +static const char * const ppi2grp[] = { "ppi2_8bgrp", + "ppi2_16bgrp" }; static const char * const lp0grp[] = { "lp0grp" }; static const char * const lp1grp[] = { "lp1grp" }; static const char * const lp2grp[] = { "lp2grp" }; static const char * const lp3grp[] = { "lp3grp" }; static const struct adi_pmx_func adi_pmx_functions[] = { - ADI_PMX_FUNCTION("uart0", uart0grp, uart0_mux), - ADI_PMX_FUNCTION("uart0_ctsrts", uart0ctsrtsgrp, uart0_ctsrts_mux), - ADI_PMX_FUNCTION("uart1", uart1grp, uart1_mux), - ADI_PMX_FUNCTION("uart1_ctsrts", uart1ctsrtsgrp, uart1_ctsrts_mux), - ADI_PMX_FUNCTION("rsi0", rsi0grp, rsi0_mux), - ADI_PMX_FUNCTION("eth0", eth0grp, eth0_mux), - ADI_PMX_FUNCTION("eth1", eth1grp, eth1_mux), - ADI_PMX_FUNCTION("spi0", spi0grp, spi0_mux), - ADI_PMX_FUNCTION("spi1", spi1grp, spi1_mux), - ADI_PMX_FUNCTION("twi0", twi0grp, twi0_mux), - ADI_PMX_FUNCTION("twi1", twi1grp, twi1_mux), - ADI_PMX_FUNCTION("rotary", rotarygrp, rotary_mux), - ADI_PMX_FUNCTION("can0", can0grp, can0_mux), - ADI_PMX_FUNCTION("smc0", smc0grp, smc0_mux), - ADI_PMX_FUNCTION("sport0", sport0grp, sport0_mux), - ADI_PMX_FUNCTION("sport1", sport1grp, sport1_mux), - ADI_PMX_FUNCTION("sport2", sport2grp, sport2_mux), - ADI_PMX_FUNCTION("ppi0_8b", ppi0_8bgrp, ppi0_8b_mux), - ADI_PMX_FUNCTION("ppi0_16b", ppi0_16bgrp, ppi0_16b_mux), - ADI_PMX_FUNCTION("ppi0_24b", ppi0_24bgrp, ppi0_24b_mux), - ADI_PMX_FUNCTION("ppi1_8b", ppi1_8bgrp, ppi1_8b_mux), - ADI_PMX_FUNCTION("ppi1_16b", ppi1_16bgrp, ppi1_16b_mux), - ADI_PMX_FUNCTION("ppi2_8b", ppi2_8bgrp, ppi2_8b_mux), - ADI_PMX_FUNCTION("ppi2_16b", ppi2_16bgrp, ppi2_16b_mux), - ADI_PMX_FUNCTION("lp0", lp0grp, lp0_mux), - ADI_PMX_FUNCTION("lp1", lp1grp, lp1_mux), - ADI_PMX_FUNCTION("lp2", lp2grp, lp2_mux), - ADI_PMX_FUNCTION("lp3", lp3grp, lp3_mux), + ADI_PMX_FUNCTION("uart0", uart0grp), + ADI_PMX_FUNCTION("uart0_ctsrts", uart0ctsrtsgrp), + ADI_PMX_FUNCTION("uart1", uart1grp), + ADI_PMX_FUNCTION("uart1_ctsrts", uart1ctsrtsgrp), + ADI_PMX_FUNCTION("rsi0", rsi0grp), + ADI_PMX_FUNCTION("eth0", eth0grp), + ADI_PMX_FUNCTION("eth1", eth1grp), + ADI_PMX_FUNCTION("spi0", spi0grp), + ADI_PMX_FUNCTION("spi1", spi1grp), + ADI_PMX_FUNCTION("twi0", twi0grp), + ADI_PMX_FUNCTION("twi1", twi1grp), + ADI_PMX_FUNCTION("rotary", rotarygrp), + ADI_PMX_FUNCTION("can0", can0grp), + ADI_PMX_FUNCTION("smc0", smc0grp), + ADI_PMX_FUNCTION("sport0", sport0grp), + ADI_PMX_FUNCTION("sport1", sport1grp), + ADI_PMX_FUNCTION("sport2", sport2grp), + ADI_PMX_FUNCTION("ppi0", ppi0grp), + ADI_PMX_FUNCTION("ppi1", ppi1grp), + ADI_PMX_FUNCTION("ppi2", ppi2grp), + ADI_PMX_FUNCTION("lp0", lp0grp), + ADI_PMX_FUNCTION("lp1", lp1grp), + ADI_PMX_FUNCTION("lp2", lp2grp), + ADI_PMX_FUNCTION("lp3", lp3grp), }; static const struct adi_pinctrl_soc_data adi_bf60x_soc = { diff --git a/drivers/pinctrl/pinctrl-adi2.c b/drivers/pinctrl/pinctrl-adi2.c index 7a39562c3e42..200ea1e72d40 100644 --- a/drivers/pinctrl/pinctrl-adi2.c +++ b/drivers/pinctrl/pinctrl-adi2.c @@ -89,6 +89,19 @@ struct gpio_port_saved { u32 mux; }; +/* + * struct gpio_pint_saved - PINT registers saved in PM operations + * + * @assign: ASSIGN register + * @edge_set: EDGE_SET register + * @invert_set: INVERT_SET register + */ +struct gpio_pint_saved { + u32 assign; + u32 edge_set; + u32 invert_set; +}; + /** * struct gpio_pint - Pin interrupt controller device. Multiple ADI GPIO * banks can be mapped into one Pin interrupt controller. @@ -114,7 +127,7 @@ struct gpio_pint { int irq; struct irq_domain *domain[2]; struct gpio_pint_regs *regs; - struct adi_pm_pint_save saved_data; + struct gpio_pint_saved saved_data; int map_count; spinlock_t lock; @@ -160,7 +173,7 @@ struct adi_pinctrl { struct gpio_port { struct list_head node; void __iomem *base; - unsigned int irq_base; + int irq_base; unsigned int width; struct gpio_port_t *regs; struct gpio_port_saved saved_data; @@ -605,8 +618,8 @@ static struct pinctrl_ops adi_pctrl_ops = { .get_group_pins = adi_get_group_pins, }; -static int adi_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector, - unsigned group) +static int adi_pinmux_enable(struct pinctrl_dev *pctldev, unsigned func_id, + unsigned group_id) { struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); struct gpio_port *port; @@ -614,7 +627,7 @@ static int adi_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector, unsigned long flags; unsigned short *mux, pin; - mux = (unsigned short *)pinctrl->soc->functions[selector].mux; + mux = (unsigned short *)pinctrl->soc->groups[group_id].mux; while (*mux) { pin = P_IDENT(*mux); @@ -628,7 +641,7 @@ static int adi_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector, spin_lock_irqsave(&port->lock, flags); portmux_setup(port, pin_to_offset(range, pin), - P_FUNCT2MUX(*mux)); + P_FUNCT2MUX(*mux)); port_setup(port, pin_to_offset(range, pin), false); mux++; @@ -638,8 +651,8 @@ static int adi_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -static void adi_pinmux_disable(struct pinctrl_dev *pctldev, unsigned selector, - unsigned group) +static void adi_pinmux_disable(struct pinctrl_dev *pctldev, unsigned func_id, + unsigned group_id) { struct adi_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev); struct gpio_port *port; @@ -647,7 +660,7 @@ static void adi_pinmux_disable(struct pinctrl_dev *pctldev, unsigned selector, unsigned long flags; unsigned short *mux, pin; - mux = (unsigned short *)pinctrl->soc->functions[selector].mux; + mux = (unsigned short *)pinctrl->soc->groups[group_id].mux; while (*mux) { pin = P_IDENT(*mux); diff --git a/drivers/pinctrl/pinctrl-adi2.h b/drivers/pinctrl/pinctrl-adi2.h index 1f06f8df1fa3..3ca29738213f 100644 --- a/drivers/pinctrl/pinctrl-adi2.h +++ b/drivers/pinctrl/pinctrl-adi2.h @@ -21,13 +21,15 @@ struct adi_pin_group { const char *name; const unsigned *pins; const unsigned num; + const unsigned short *mux; }; -#define ADI_PIN_GROUP(n, p) \ +#define ADI_PIN_GROUP(n, p, m) \ { \ .name = n, \ .pins = p, \ .num = ARRAY_SIZE(p), \ + .mux = m, \ } /** @@ -41,15 +43,13 @@ struct adi_pmx_func { const char *name; const char * const *groups; const unsigned num_groups; - const unsigned short *mux; }; -#define ADI_PMX_FUNCTION(n, g, m) \ +#define ADI_PMX_FUNCTION(n, g) \ { \ .name = n, \ .groups = g, \ .num_groups = ARRAY_SIZE(g), \ - .mux = m, \ } /** diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index d990e33d8aa7..5d24aaec5dbc 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1137,6 +1137,17 @@ static void at91_gpio_free(struct gpio_chip *chip, unsigned offset) pinctrl_free_gpio(gpio); } +static int at91_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); + void __iomem *pio = at91_gpio->regbase; + unsigned mask = 1 << offset; + u32 osr; + + osr = readl_relaxed(pio + PIO_OSR); + return !(osr & mask); +} + static int at91_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip); @@ -1325,6 +1336,31 @@ static int alt_gpio_irq_type(struct irq_data *d, unsigned type) return 0; } +static unsigned int gpio_irq_startup(struct irq_data *d) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + unsigned pin = d->hwirq; + int ret; + + ret = gpio_lock_as_irq(&at91_gpio->chip, pin); + if (ret) { + dev_err(at91_gpio->chip.dev, "unable to lock pind %lu IRQ\n", + d->hwirq); + return ret; + } + gpio_irq_unmask(d); + return 0; +} + +static void gpio_irq_shutdown(struct irq_data *d) +{ + struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d); + unsigned pin = d->hwirq; + + gpio_irq_mask(d); + gpio_unlock_as_irq(&at91_gpio->chip, pin); +} + #ifdef CONFIG_PM static u32 wakeups[MAX_GPIO_BANKS]; @@ -1399,6 +1435,8 @@ void at91_pinctrl_gpio_resume(void) static struct irq_chip gpio_irqchip = { .name = "GPIO", + .irq_startup = gpio_irq_startup, + .irq_shutdown = gpio_irq_shutdown, .irq_disable = gpio_irq_mask, .irq_mask = gpio_irq_mask, .irq_unmask = gpio_irq_unmask, @@ -1543,6 +1581,7 @@ static int at91_gpio_of_irq_setup(struct device_node *node, static struct gpio_chip at91_gpio_template = { .request = at91_gpio_request, .free = at91_gpio_free, + .get_direction = at91_gpio_get_direction, .direction_input = at91_gpio_direction_input, .get = at91_gpio_get, .direction_output = at91_gpio_direction_output, diff --git a/drivers/pinctrl/pinctrl-baytrail.c b/drivers/pinctrl/pinctrl-baytrail.c index 665b96bc0c3a..bf2b3f655469 100644 --- a/drivers/pinctrl/pinctrl-baytrail.c +++ b/drivers/pinctrl/pinctrl-baytrail.c @@ -60,6 +60,10 @@ #define BYT_NGPIO_NCORE 28 #define BYT_NGPIO_SUS 44 +#define BYT_SCORE_ACPI_UID "1" +#define BYT_NCORE_ACPI_UID "2" +#define BYT_SUS_ACPI_UID "3" + /* * Baytrail gpio controller consist of three separate sub-controllers called * SCORE, NCORE and SUS. The sub-controllers are identified by their acpi UID. @@ -102,17 +106,17 @@ static unsigned const sus_pins[BYT_NGPIO_SUS] = { static struct pinctrl_gpio_range byt_ranges[] = { { - .name = "1", /* match with acpi _UID in probe */ + .name = BYT_SCORE_ACPI_UID, /* match with acpi _UID in probe */ .npins = BYT_NGPIO_SCORE, .pins = score_pins, }, { - .name = "2", + .name = BYT_NCORE_ACPI_UID, .npins = BYT_NGPIO_NCORE, .pins = ncore_pins, }, { - .name = "3", + .name = BYT_SUS_ACPI_UID, .npins = BYT_NGPIO_SUS, .pins = sus_pins, }, @@ -145,9 +149,41 @@ static void __iomem *byt_gpio_reg(struct gpio_chip *chip, unsigned offset, return vg->reg_base + reg_offset + reg; } +static bool is_special_pin(struct byt_gpio *vg, unsigned offset) +{ + /* SCORE pin 92-93 */ + if (!strcmp(vg->range->name, BYT_SCORE_ACPI_UID) && + offset >= 92 && offset <= 93) + return true; + + /* SUS pin 11-21 */ + if (!strcmp(vg->range->name, BYT_SUS_ACPI_UID) && + offset >= 11 && offset <= 21) + return true; + + return false; +} + static int byt_gpio_request(struct gpio_chip *chip, unsigned offset) { struct byt_gpio *vg = to_byt_gpio(chip); + void __iomem *reg = byt_gpio_reg(chip, offset, BYT_CONF0_REG); + u32 value; + bool special; + + /* + * In most cases, func pin mux 000 means GPIO function. + * But, some pins may have func pin mux 001 represents + * GPIO function. Only allow user to export pin with + * func pin mux preset as GPIO function by BIOS/FW. + */ + value = readl(reg) & BYT_PIN_MUX; + special = is_special_pin(vg, offset); + if ((special && value != 1) || (!special && value)) { + dev_err(&vg->pdev->dev, + "pin %u cannot be used as GPIO.\n", offset); + return -EINVAL; + } pm_runtime_get(&vg->pdev->dev); diff --git a/drivers/pinctrl/pinctrl-capri.c b/drivers/pinctrl/pinctrl-capri.c index 4669c53f99b0..eb2500212147 100644 --- a/drivers/pinctrl/pinctrl-capri.c +++ b/drivers/pinctrl/pinctrl-capri.c @@ -1435,7 +1435,7 @@ int __init capri_pinctrl_probe(struct platform_device *pdev) } static struct of_device_id capri_pinctrl_of_match[] = { - { .compatible = "brcm,capri-pinctrl", }, + { .compatible = "brcm,bcm11351-pinctrl", }, { }, }; diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 155b1b3a0e7a..07c81306f2f3 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -1042,6 +1042,88 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = { }, }; +/* pin banks of exynos5260 pin-controller 0 */ +static struct samsung_pin_bank exynos5260_pin_banks0[] = { + EXYNOS_PIN_BANK_EINTG(4, 0x000, "gpa0", 0x00), + EXYNOS_PIN_BANK_EINTG(7, 0x020, "gpa1", 0x04), + EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpa2", 0x08), + EXYNOS_PIN_BANK_EINTG(5, 0x060, "gpb0", 0x0c), + EXYNOS_PIN_BANK_EINTG(4, 0x080, "gpb1", 0x10), + EXYNOS_PIN_BANK_EINTG(5, 0x0a0, "gpb2", 0x14), + EXYNOS_PIN_BANK_EINTG(8, 0x0c0, "gpb3", 0x18), + EXYNOS_PIN_BANK_EINTG(8, 0x0e0, "gpb4", 0x1c), + EXYNOS_PIN_BANK_EINTG(8, 0x100, "gpb5", 0x20), + EXYNOS_PIN_BANK_EINTG(8, 0x120, "gpd0", 0x24), + EXYNOS_PIN_BANK_EINTG(7, 0x140, "gpd1", 0x28), + EXYNOS_PIN_BANK_EINTG(5, 0x160, "gpd2", 0x2c), + EXYNOS_PIN_BANK_EINTG(8, 0x180, "gpe0", 0x30), + EXYNOS_PIN_BANK_EINTG(5, 0x1a0, "gpe1", 0x34), + EXYNOS_PIN_BANK_EINTG(4, 0x1c0, "gpf0", 0x38), + EXYNOS_PIN_BANK_EINTG(8, 0x1e0, "gpf1", 0x3c), + EXYNOS_PIN_BANK_EINTG(2, 0x200, "gpk0", 0x40), + EXYNOS_PIN_BANK_EINTW(8, 0xc00, "gpx0", 0x00), + EXYNOS_PIN_BANK_EINTW(8, 0xc20, "gpx1", 0x04), + EXYNOS_PIN_BANK_EINTW(8, 0xc40, "gpx2", 0x08), + EXYNOS_PIN_BANK_EINTW(8, 0xc60, "gpx3", 0x0c), +}; + +/* pin banks of exynos5260 pin-controller 1 */ +static struct samsung_pin_bank exynos5260_pin_banks1[] = { + EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpc0", 0x00), + EXYNOS_PIN_BANK_EINTG(6, 0x020, "gpc1", 0x04), + EXYNOS_PIN_BANK_EINTG(7, 0x040, "gpc2", 0x08), + EXYNOS_PIN_BANK_EINTG(4, 0x060, "gpc3", 0x0c), + EXYNOS_PIN_BANK_EINTG(4, 0x080, "gpc4", 0x10), +}; + +/* pin banks of exynos5260 pin-controller 2 */ +static struct samsung_pin_bank exynos5260_pin_banks2[] = { + EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpz0", 0x00), + EXYNOS_PIN_BANK_EINTG(4, 0x020, "gpz1", 0x04), +}; + +/* + * Samsung pinctrl driver data for Exynos5260 SoC. Exynos5260 SoC includes + * three gpio/pin-mux/pinconfig controllers. + */ +struct samsung_pin_ctrl exynos5260_pin_ctrl[] = { + { + /* pin-controller instance 0 data */ + .pin_banks = exynos5260_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos5260_pin_banks0), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .weint_con = EXYNOS_WKUP_ECON_OFFSET, + .weint_mask = EXYNOS_WKUP_EMASK_OFFSET, + .weint_pend = EXYNOS_WKUP_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .eint_wkup_init = exynos_eint_wkup_init, + .label = "exynos5260-gpio-ctrl0", + }, { + /* pin-controller instance 1 data */ + .pin_banks = exynos5260_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos5260_pin_banks1), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos5260-gpio-ctrl1", + }, { + /* pin-controller instance 2 data */ + .pin_banks = exynos5260_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos5260_pin_banks2), + .geint_con = EXYNOS_GPIO_ECON_OFFSET, + .geint_mask = EXYNOS_GPIO_EMASK_OFFSET, + .geint_pend = EXYNOS_GPIO_EPEND_OFFSET, + .svc = EXYNOS_SVC_OFFSET, + .eint_gpio_init = exynos_eint_gpio_init, + .label = "exynos5260-gpio-ctrl2", + }, +}; + /* pin banks of exynos5420 pin-controller 0 */ static struct samsung_pin_bank exynos5420_pin_banks0[] = { EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpy7", 0x00), diff --git a/drivers/pinctrl/pinctrl-imx.c b/drivers/pinctrl/pinctrl-imx.c index 4779b8e0eee8..e118fb121e02 100644 --- a/drivers/pinctrl/pinctrl-imx.c +++ b/drivers/pinctrl/pinctrl-imx.c @@ -491,7 +491,7 @@ static int imx_pinctrl_parse_groups(struct device_node *np, pin->mux_mode |= IOMUXC_CONFIG_SION; pin->config = config & ~IMX_PAD_SION; - dev_dbg(info->dev, "%s: %d 0x%08lx", info->pins[i].name, + dev_dbg(info->dev, "%s: %d 0x%08lx", info->pins[pin_id].name, pin->mux_mode, pin->config); } diff --git a/drivers/pinctrl/pinctrl-msm.c b/drivers/pinctrl/pinctrl-msm.c index ef2bf3126da6..343f421c7696 100644 --- a/drivers/pinctrl/pinctrl-msm.c +++ b/drivers/pinctrl/pinctrl-msm.c @@ -28,7 +28,6 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqchip/chained_irq.h> -#include <linux/of_irq.h> #include <linux/spinlock.h> #include "core.h" @@ -50,7 +49,6 @@ * @enabled_irqs: Bitmap of currently enabled irqs. * @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge * detection. - * @wake_irqs: Bitmap of irqs with requested as wakeup source. * @soc; Reference to soc_data of platform specific data. * @regs: Base address for the TLMM register map. */ @@ -65,7 +63,6 @@ struct msm_pinctrl { DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO); DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO); - DECLARE_BITMAP(wake_irqs, MAX_NR_GPIO); const struct msm_pinctrl_soc_data *soc; void __iomem *regs; @@ -203,42 +200,29 @@ static const struct pinmux_ops msm_pinmux_ops = { static int msm_config_reg(struct msm_pinctrl *pctrl, const struct msm_pingroup *g, unsigned param, - s16 *reg, unsigned *mask, unsigned *bit) { switch (param) { case PIN_CONFIG_BIAS_DISABLE: - *reg = g->ctl_reg; - *bit = g->pull_bit; - *mask = 3; - break; case PIN_CONFIG_BIAS_PULL_DOWN: - *reg = g->ctl_reg; - *bit = g->pull_bit; - *mask = 3; - break; case PIN_CONFIG_BIAS_PULL_UP: - *reg = g->ctl_reg; *bit = g->pull_bit; *mask = 3; break; case PIN_CONFIG_DRIVE_STRENGTH: - *reg = g->ctl_reg; *bit = g->drv_bit; *mask = 7; break; + case PIN_CONFIG_OUTPUT: + *bit = g->oe_bit; + *mask = 1; + break; default: dev_err(pctrl->dev, "Invalid config param %04x\n", param); return -ENOTSUPP; } - if (*reg < 0) { - dev_err(pctrl->dev, "Config param %04x not supported on group %s\n", - param, g->name); - return -ENOTSUPP; - } - return 0; } @@ -261,8 +245,10 @@ static int msm_config_set(struct pinctrl_dev *pctldev, unsigned int pin, #define MSM_PULL_DOWN 1 #define MSM_PULL_UP 3 -static const unsigned msm_regval_to_drive[] = { 2, 4, 6, 8, 10, 12, 14, 16 }; -static const unsigned msm_drive_to_regval[] = { -1, -1, 0, -1, 1, -1, 2, -1, 3, -1, 4, -1, 5, -1, 6, -1, 7 }; +static unsigned msm_regval_to_drive(u32 val) +{ + return (val + 1) * 2; +} static int msm_config_group_get(struct pinctrl_dev *pctldev, unsigned int group, @@ -274,17 +260,16 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, unsigned mask; unsigned arg; unsigned bit; - s16 reg; int ret; u32 val; g = &pctrl->soc->groups[group]; - ret = msm_config_reg(pctrl, g, param, ®, &mask, &bit); + ret = msm_config_reg(pctrl, g, param, &mask, &bit); if (ret < 0) return ret; - val = readl(pctrl->regs + reg); + val = readl(pctrl->regs + g->ctl_reg); arg = (val >> bit) & mask; /* Convert register value to pinconf value */ @@ -299,7 +284,15 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, arg = arg == MSM_PULL_UP; break; case PIN_CONFIG_DRIVE_STRENGTH: - arg = msm_regval_to_drive[arg]; + arg = msm_regval_to_drive(arg); + break; + case PIN_CONFIG_OUTPUT: + /* Pin is not output */ + if (!arg) + return -EINVAL; + + val = readl(pctrl->regs + g->io_reg); + arg = !!(val & BIT(g->in_bit)); break; default: dev_err(pctrl->dev, "Unsupported config parameter: %x\n", @@ -324,7 +317,6 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, unsigned mask; unsigned arg; unsigned bit; - s16 reg; int ret; u32 val; int i; @@ -335,7 +327,7 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, param = pinconf_to_config_param(configs[i]); arg = pinconf_to_config_argument(configs[i]); - ret = msm_config_reg(pctrl, g, param, ®, &mask, &bit); + ret = msm_config_reg(pctrl, g, param, &mask, &bit); if (ret < 0) return ret; @@ -352,10 +344,24 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, break; case PIN_CONFIG_DRIVE_STRENGTH: /* Check for invalid values */ - if (arg >= ARRAY_SIZE(msm_drive_to_regval)) + if (arg > 16 || arg < 2 || (arg % 2) != 0) arg = -1; else - arg = msm_drive_to_regval[arg]; + arg = (arg / 2) - 1; + break; + case PIN_CONFIG_OUTPUT: + /* set output value */ + spin_lock_irqsave(&pctrl->lock, flags); + val = readl(pctrl->regs + g->io_reg); + if (arg) + val |= BIT(g->out_bit); + else + val &= ~BIT(g->out_bit); + writel(val, pctrl->regs + g->io_reg); + spin_unlock_irqrestore(&pctrl->lock, flags); + + /* enable output */ + arg = 1; break; default: dev_err(pctrl->dev, "Unsupported config parameter: %x\n", @@ -370,10 +376,10 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, } spin_lock_irqsave(&pctrl->lock, flags); - val = readl(pctrl->regs + reg); + val = readl(pctrl->regs + g->ctl_reg); val &= ~(mask << bit); val |= arg << bit; - writel(val, pctrl->regs + reg); + writel(val, pctrl->regs + g->ctl_reg); spin_unlock_irqrestore(&pctrl->lock, flags); } @@ -402,8 +408,6 @@ static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset) u32 val; g = &pctrl->soc->groups[offset]; - if (WARN_ON(g->io_reg < 0)) - return -EINVAL; spin_lock_irqsave(&pctrl->lock, flags); @@ -424,8 +428,6 @@ static int msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, in u32 val; g = &pctrl->soc->groups[offset]; - if (WARN_ON(g->io_reg < 0)) - return -EINVAL; spin_lock_irqsave(&pctrl->lock, flags); @@ -452,8 +454,6 @@ static int msm_gpio_get(struct gpio_chip *chip, unsigned offset) u32 val; g = &pctrl->soc->groups[offset]; - if (WARN_ON(g->io_reg < 0)) - return -EINVAL; val = readl(pctrl->regs + g->io_reg); return !!(val & BIT(g->in_bit)); @@ -467,8 +467,6 @@ static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) u32 val; g = &pctrl->soc->groups[offset]; - if (WARN_ON(g->io_reg < 0)) - return; spin_lock_irqsave(&pctrl->lock, flags); @@ -534,7 +532,7 @@ static void msm_gpio_dbg_show_one(struct seq_file *s, pull = (ctl_reg >> g->pull_bit) & 3; seq_printf(s, " %-8s: %-3s %d", g->name, is_out ? "out" : "in", func); - seq_printf(s, " %dmA", msm_regval_to_drive[drive]); + seq_printf(s, " %dmA", msm_regval_to_drive(drive)); seq_printf(s, " %s", pulls[pull]); } @@ -617,8 +615,6 @@ static void msm_gpio_irq_mask(struct irq_data *d) pctrl = irq_data_get_irq_chip_data(d); g = &pctrl->soc->groups[d->hwirq]; - if (WARN_ON(g->intr_cfg_reg < 0)) - return; spin_lock_irqsave(&pctrl->lock, flags); @@ -640,8 +636,6 @@ static void msm_gpio_irq_unmask(struct irq_data *d) pctrl = irq_data_get_irq_chip_data(d); g = &pctrl->soc->groups[d->hwirq]; - if (WARN_ON(g->intr_status_reg < 0)) - return; spin_lock_irqsave(&pctrl->lock, flags); @@ -667,8 +661,6 @@ static void msm_gpio_irq_ack(struct irq_data *d) pctrl = irq_data_get_irq_chip_data(d); g = &pctrl->soc->groups[d->hwirq]; - if (WARN_ON(g->intr_status_reg < 0)) - return; spin_lock_irqsave(&pctrl->lock, flags); @@ -693,8 +685,6 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) pctrl = irq_data_get_irq_chip_data(d); g = &pctrl->soc->groups[d->hwirq]; - if (WARN_ON(g->intr_cfg_reg < 0)) - return -EINVAL; spin_lock_irqsave(&pctrl->lock, flags); @@ -783,22 +773,12 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) { struct msm_pinctrl *pctrl; unsigned long flags; - unsigned ngpio; pctrl = irq_data_get_irq_chip_data(d); - ngpio = pctrl->chip.ngpio; spin_lock_irqsave(&pctrl->lock, flags); - if (on) { - if (bitmap_empty(pctrl->wake_irqs, ngpio)) - enable_irq_wake(pctrl->irq); - set_bit(d->hwirq, pctrl->wake_irqs); - } else { - clear_bit(d->hwirq, pctrl->wake_irqs); - if (bitmap_empty(pctrl->wake_irqs, ngpio)) - disable_irq_wake(pctrl->irq); - } + irq_set_irq_wake(pctrl->irq, on); spin_unlock_irqrestore(&pctrl->lock, flags); @@ -869,6 +849,12 @@ static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } +/* + * This lock class tells lockdep that GPIO irqs are in a different + * category than their parents, so it won't report false recursion. + */ +static struct lock_class_key gpio_lock_class; + static int msm_gpio_init(struct msm_pinctrl *pctrl) { struct gpio_chip *chip; @@ -876,10 +862,14 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) int ret; int i; int r; + unsigned ngpio = pctrl->soc->ngpios; + + if (WARN_ON(ngpio > MAX_NR_GPIO)) + return -EINVAL; chip = &pctrl->chip; chip->base = 0; - chip->ngpio = pctrl->soc->ngpios; + chip->ngpio = ngpio; chip->label = dev_name(pctrl->dev); chip->dev = pctrl->dev; chip->owner = THIS_MODULE; @@ -907,6 +897,7 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) for (i = 0; i < chip->ngpio; i++) { irq = irq_create_mapping(pctrl->domain, i); + irq_set_lockdep_class(irq, &gpio_lock_class); irq_set_chip_and_handler(irq, &msm_gpio_irq_chip, handle_edge_irq); irq_set_chip_data(irq, pctrl); } diff --git a/drivers/pinctrl/pinctrl-msm.h b/drivers/pinctrl/pinctrl-msm.h index 206e782e2daa..8fbe9fb19f36 100644 --- a/drivers/pinctrl/pinctrl-msm.h +++ b/drivers/pinctrl/pinctrl-msm.h @@ -13,10 +13,7 @@ #ifndef __PINCTRL_MSM_H__ #define __PINCTRL_MSM_H__ -#include <linux/pinctrl/pinctrl.h> -#include <linux/pinctrl/pinmux.h> -#include <linux/pinctrl/pinconf.h> -#include <linux/pinctrl/machine.h> +struct pinctrl_pin_desc; /** * struct msm_function - a pinmux function diff --git a/drivers/pinctrl/pinctrl-msm8x74.c b/drivers/pinctrl/pinctrl-msm8x74.c index f944bf2172ef..dde5529807aa 100644 --- a/drivers/pinctrl/pinctrl-msm8x74.c +++ b/drivers/pinctrl/pinctrl-msm8x74.c @@ -15,7 +15,6 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pinctrl/pinctrl.h> -#include <linux/pinctrl/pinmux.h> #include "pinctrl-msm.h" @@ -406,6 +405,7 @@ enum msm8x74_functions { MSM_MUX_blsp_i2c6, MSM_MUX_blsp_i2c11, MSM_MUX_blsp_spi1, + MSM_MUX_blsp_spi8, MSM_MUX_blsp_uart2, MSM_MUX_blsp_uart8, MSM_MUX_slimbus, @@ -416,6 +416,9 @@ static const char * const blsp_i2c2_groups[] = { "gpio6", "gpio7" }; static const char * const blsp_i2c6_groups[] = { "gpio29", "gpio30" }; static const char * const blsp_i2c11_groups[] = { "gpio83", "gpio84" }; static const char * const blsp_spi1_groups[] = { "gpio0", "gpio1", "gpio2", "gpio3" }; +static const char * const blsp_spi8_groups[] = { + "gpio45", "gpio46", "gpio47", "gpio48" +}; static const char * const blsp_uart2_groups[] = { "gpio4", "gpio5" }; static const char * const blsp_uart8_groups[] = { "gpio45", "gpio46" }; static const char * const slimbus_groups[] = { "gpio70", "gpio71" }; @@ -425,6 +428,7 @@ static const struct msm_function msm8x74_functions[] = { FUNCTION(blsp_i2c6), FUNCTION(blsp_i2c11), FUNCTION(blsp_spi1), + FUNCTION(blsp_spi8), FUNCTION(blsp_uart2), FUNCTION(blsp_uart8), FUNCTION(slimbus), @@ -476,10 +480,10 @@ static const struct msm_pingroup msm8x74_groups[] = { PINGROUP(42, NA, NA, NA, NA, NA, NA, NA), PINGROUP(43, NA, NA, NA, NA, NA, NA, NA), PINGROUP(44, NA, NA, NA, NA, NA, NA, NA), - PINGROUP(45, NA, blsp_uart8, NA, NA, NA, NA, NA), - PINGROUP(46, NA, blsp_uart8, NA, NA, NA, NA, NA), - PINGROUP(47, NA, NA, NA, NA, NA, NA, NA), - PINGROUP(48, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(45, blsp_spi8, blsp_uart8, NA, NA, NA, NA, NA), + PINGROUP(46, blsp_spi8, blsp_uart8, NA, NA, NA, NA, NA), + PINGROUP(47, blsp_spi8, NA, NA, NA, NA, NA, NA), + PINGROUP(48, blsp_spi8, NA, NA, NA, NA, NA, NA), PINGROUP(49, NA, NA, NA, NA, NA, NA, NA), PINGROUP(50, NA, NA, NA, NA, NA, NA, NA), PINGROUP(51, NA, NA, NA, NA, NA, NA, NA), diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index 53a11114927f..cec7762cf335 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -2035,27 +2035,29 @@ static const struct of_device_id nmk_pinctrl_match[] = { {}, }; -static int nmk_pinctrl_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int nmk_pinctrl_suspend(struct device *dev) { struct nmk_pinctrl *npct; - npct = platform_get_drvdata(pdev); + npct = dev_get_drvdata(dev); if (!npct) return -EINVAL; return pinctrl_force_sleep(npct->pctl); } -static int nmk_pinctrl_resume(struct platform_device *pdev) +static int nmk_pinctrl_resume(struct device *dev) { struct nmk_pinctrl *npct; - npct = platform_get_drvdata(pdev); + npct = dev_get_drvdata(dev); if (!npct) return -EINVAL; return pinctrl_force_default(npct->pctl); } +#endif static int nmk_pinctrl_probe(struct platform_device *pdev) { @@ -2144,17 +2146,18 @@ static struct platform_driver nmk_gpio_driver = { .probe = nmk_gpio_probe, }; +static SIMPLE_DEV_PM_OPS(nmk_pinctrl_pm_ops, + nmk_pinctrl_suspend, + nmk_pinctrl_resume); + static struct platform_driver nmk_pinctrl_driver = { .driver = { .owner = THIS_MODULE, .name = "pinctrl-nomadik", .of_match_table = nmk_pinctrl_match, + .pm = &nmk_pinctrl_pm_ops, }, .probe = nmk_pinctrl_probe, -#ifdef CONFIG_PM - .suspend = nmk_pinctrl_suspend, - .resume = nmk_pinctrl_resume, -#endif }; static int __init nmk_gpio_init(void) diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 47ec2e8741e4..0324d4cb19b2 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -1120,6 +1120,8 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = { .data = (void *)exynos4x12_pin_ctrl }, { .compatible = "samsung,exynos5250-pinctrl", .data = (void *)exynos5250_pin_ctrl }, + { .compatible = "samsung,exynos5260-pinctrl", + .data = (void *)exynos5260_pin_ctrl }, { .compatible = "samsung,exynos5420-pinctrl", .data = (void *)exynos5420_pin_ctrl }, { .compatible = "samsung,s5pv210-pinctrl", diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index 30622d9afa2e..bab9c2122556 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -254,6 +254,7 @@ struct samsung_pmx_func { extern struct samsung_pin_ctrl exynos4210_pin_ctrl[]; extern struct samsung_pin_ctrl exynos4x12_pin_ctrl[]; extern struct samsung_pin_ctrl exynos5250_pin_ctrl[]; +extern struct samsung_pin_ctrl exynos5260_pin_ctrl[]; extern struct samsung_pin_ctrl exynos5420_pin_ctrl[]; extern struct samsung_pin_ctrl s3c64xx_pin_ctrl[]; extern struct samsung_pin_ctrl s3c2412_pin_ctrl[]; diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index de6459628b4f..81075f2a1d3f 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -662,6 +662,7 @@ static int pcs_pinconf_get(struct pinctrl_dev *pctldev, break; case PIN_CONFIG_DRIVE_STRENGTH: case PIN_CONFIG_SLEW_RATE: + case PIN_CONFIG_LOW_POWER_MODE: default: *config = data; break; @@ -699,6 +700,7 @@ static int pcs_pinconf_set(struct pinctrl_dev *pctldev, case PIN_CONFIG_INPUT_SCHMITT: case PIN_CONFIG_DRIVE_STRENGTH: case PIN_CONFIG_SLEW_RATE: + case PIN_CONFIG_LOW_POWER_MODE: shift = ffs(func->conf[i].mask) - 1; data &= ~func->conf[i].mask; data |= (arg << shift) & func->conf[i].mask; @@ -1101,6 +1103,7 @@ static int pcs_parse_pinconf(struct pcs_device *pcs, struct device_node *np, { "pinctrl-single,drive-strength", PIN_CONFIG_DRIVE_STRENGTH, }, { "pinctrl-single,slew-rate", PIN_CONFIG_SLEW_RATE, }, { "pinctrl-single,input-schmitt", PIN_CONFIG_INPUT_SCHMITT, }, + { "pinctrl-single,low-power-mode", PIN_CONFIG_LOW_POWER_MODE, }, }; struct pcs_conf_type prop4[] = { { "pinctrl-single,bias-pullup", PIN_CONFIG_BIAS_PULL_UP, }, diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index 320c27363cc8..bd725b0a4341 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -13,7 +13,12 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdesc.h> +#include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/of.h> +#include <linux/of_irq.h> #include <linux/of_gpio.h> #include <linux/of_address.h> #include <linux/regmap.h> @@ -266,11 +271,59 @@ struct st_pctl_group { struct st_pinconf *pin_conf; }; +/* + * Edge triggers are not supported at hardware level, it is supported by + * software by exploiting the level trigger support in hardware. + * Software uses a virtual register (EDGE_CONF) for edge trigger configuration + * of each gpio pin in a GPIO bank. + * + * Each bank has a 32 bit EDGE_CONF register which is divided in to 8 parts of + * 4-bits. Each 4-bit space is allocated for each pin in a gpio bank. + * + * bit allocation per pin is: + * Bits: [0 - 3] | [4 - 7] [8 - 11] ... ... ... ... [ 28 - 31] + * -------------------------------------------------------- + * | pin-0 | pin-2 | pin-3 | ... ... ... ... | pin -7 | + * -------------------------------------------------------- + * + * A pin can have one of following the values in its edge configuration field. + * + * ------- ---------------------------- + * [0-3] - Description + * ------- ---------------------------- + * 0000 - No edge IRQ. + * 0001 - Falling edge IRQ. + * 0010 - Rising edge IRQ. + * 0011 - Rising and Falling edge IRQ. + * ------- ---------------------------- + */ + +#define ST_IRQ_EDGE_CONF_BITS_PER_PIN 4 +#define ST_IRQ_EDGE_MASK 0xf +#define ST_IRQ_EDGE_FALLING BIT(0) +#define ST_IRQ_EDGE_RISING BIT(1) +#define ST_IRQ_EDGE_BOTH (BIT(0) | BIT(1)) + +#define ST_IRQ_RISING_EDGE_CONF(pin) \ + (ST_IRQ_EDGE_RISING << (pin * ST_IRQ_EDGE_CONF_BITS_PER_PIN)) + +#define ST_IRQ_FALLING_EDGE_CONF(pin) \ + (ST_IRQ_EDGE_FALLING << (pin * ST_IRQ_EDGE_CONF_BITS_PER_PIN)) + +#define ST_IRQ_BOTH_EDGE_CONF(pin) \ + (ST_IRQ_EDGE_BOTH << (pin * ST_IRQ_EDGE_CONF_BITS_PER_PIN)) + +#define ST_IRQ_EDGE_CONF(conf, pin) \ + (conf >> (pin * ST_IRQ_EDGE_CONF_BITS_PER_PIN) & ST_IRQ_EDGE_MASK) + struct st_gpio_bank { struct gpio_chip gpio_chip; struct pinctrl_gpio_range range; void __iomem *base; struct st_pio_control pc; + struct irq_domain *domain; + unsigned long irq_edge_conf; + spinlock_t lock; }; struct st_pinctrl { @@ -284,6 +337,7 @@ struct st_pinctrl { int ngroups; struct regmap *regmap; const struct st_pctl_data *data; + void __iomem *irqmux_base; }; /* SOC specific data */ @@ -330,12 +384,25 @@ static unsigned int stih416_delays[] = {0, 300, 500, 750, 1000, 1250, 1500, static const struct st_pctl_data stih416_data = { .rt_style = st_retime_style_dedicated, .input_delays = stih416_delays, - .ninput_delays = 14, + .ninput_delays = ARRAY_SIZE(stih416_delays), .output_delays = stih416_delays, - .noutput_delays = 14, + .noutput_delays = ARRAY_SIZE(stih416_delays), .alt = 0, .oe = 40, .pu = 50, .od = 60, .rt = 100, }; +static const struct st_pctl_data stih407_flashdata = { + .rt_style = st_retime_style_none, + .input_delays = stih416_delays, + .ninput_delays = ARRAY_SIZE(stih416_delays), + .output_delays = stih416_delays, + .noutput_delays = ARRAY_SIZE(stih416_delays), + .alt = 0, + .oe = -1, /* Not Available */ + .pu = -1, /* Not Available */ + .od = 60, + .rt = 100, +}; + /* Low level functions.. */ static inline int st_gpio_bank(int gpio) { @@ -356,25 +423,29 @@ static void st_pinconf_set_config(struct st_pio_control *pc, unsigned int oe_value, pu_value, od_value; unsigned long mask = BIT(pin); - regmap_field_read(output_enable, &oe_value); - regmap_field_read(pull_up, &pu_value); - regmap_field_read(open_drain, &od_value); - - /* Clear old values */ - oe_value &= ~mask; - pu_value &= ~mask; - od_value &= ~mask; - - if (config & ST_PINCONF_OE) - oe_value |= mask; - if (config & ST_PINCONF_PU) - pu_value |= mask; - if (config & ST_PINCONF_OD) - od_value |= mask; - - regmap_field_write(output_enable, oe_value); - regmap_field_write(pull_up, pu_value); - regmap_field_write(open_drain, od_value); + if (output_enable) { + regmap_field_read(output_enable, &oe_value); + oe_value &= ~mask; + if (config & ST_PINCONF_OE) + oe_value |= mask; + regmap_field_write(output_enable, oe_value); + } + + if (pull_up) { + regmap_field_read(pull_up, &pu_value); + pu_value &= ~mask; + if (config & ST_PINCONF_PU) + pu_value |= mask; + regmap_field_write(pull_up, pu_value); + } + + if (open_drain) { + regmap_field_read(open_drain, &od_value); + od_value &= ~mask; + if (config & ST_PINCONF_OD) + od_value |= mask; + regmap_field_write(open_drain, od_value); + } } static void st_pctl_set_function(struct st_pio_control *pc, @@ -385,6 +456,9 @@ static void st_pctl_set_function(struct st_pio_control *pc, int pin = st_gpio_pin(pin_id); int offset = pin * 4; + if (!alt) + return; + regmap_field_read(alt, &val); val &= ~(0xf << offset); val |= function << offset; @@ -522,17 +596,23 @@ static void st_pinconf_get_direction(struct st_pio_control *pc, { unsigned int oe_value, pu_value, od_value; - regmap_field_read(pc->oe, &oe_value); - regmap_field_read(pc->pu, &pu_value); - regmap_field_read(pc->od, &od_value); + if (pc->oe) { + regmap_field_read(pc->oe, &oe_value); + if (oe_value & BIT(pin)) + ST_PINCONF_PACK_OE(*config); + } - if (oe_value & BIT(pin)) - ST_PINCONF_PACK_OE(*config); - if (pu_value & BIT(pin)) - ST_PINCONF_PACK_PU(*config); - if (od_value & BIT(pin)) - ST_PINCONF_PACK_OD(*config); + if (pc->pu) { + regmap_field_read(pc->pu, &pu_value); + if (pu_value & BIT(pin)) + ST_PINCONF_PACK_PU(*config); + } + if (pc->od) { + regmap_field_read(pc->od, &od_value); + if (od_value & BIT(pin)) + ST_PINCONF_PACK_OD(*config); + } } static int st_pinconf_get_retime_packed(struct st_pinctrl *info, @@ -1051,8 +1131,21 @@ static int st_pctl_dt_setup_retime(struct st_pinctrl *info, return -EINVAL; } -static int st_parse_syscfgs(struct st_pinctrl *info, - int bank, struct device_node *np) + +static struct regmap_field *st_pc_get_value(struct device *dev, + struct regmap *regmap, int bank, + int data, int lsb, int msb) +{ + struct reg_field reg = REG_FIELD((data + bank) * 4, lsb, msb); + + if (data < 0) + return NULL; + + return devm_regmap_field_alloc(dev, regmap, reg); +} + +static void st_parse_syscfgs(struct st_pinctrl *info, int bank, + struct device_node *np) { const struct st_pctl_data *data = info->data; /** @@ -1062,29 +1155,21 @@ static int st_parse_syscfgs(struct st_pinctrl *info, */ int lsb = (bank%4) * ST_GPIO_PINS_PER_BANK; int msb = lsb + ST_GPIO_PINS_PER_BANK - 1; - struct reg_field alt_reg = REG_FIELD((data->alt + bank) * 4, 0, 31); - struct reg_field oe_reg = REG_FIELD((data->oe + bank/4) * 4, lsb, msb); - struct reg_field pu_reg = REG_FIELD((data->pu + bank/4) * 4, lsb, msb); - struct reg_field od_reg = REG_FIELD((data->od + bank/4) * 4, lsb, msb); struct st_pio_control *pc = &info->banks[bank].pc; struct device *dev = info->dev; struct regmap *regmap = info->regmap; - pc->alt = devm_regmap_field_alloc(dev, regmap, alt_reg); - pc->oe = devm_regmap_field_alloc(dev, regmap, oe_reg); - pc->pu = devm_regmap_field_alloc(dev, regmap, pu_reg); - pc->od = devm_regmap_field_alloc(dev, regmap, od_reg); - - if (IS_ERR(pc->alt) || IS_ERR(pc->oe) || - IS_ERR(pc->pu) || IS_ERR(pc->od)) - return -EINVAL; + pc->alt = st_pc_get_value(dev, regmap, bank, data->alt, 0, 31); + pc->oe = st_pc_get_value(dev, regmap, bank/4, data->oe, lsb, msb); + pc->pu = st_pc_get_value(dev, regmap, bank/4, data->pu, lsb, msb); + pc->od = st_pc_get_value(dev, regmap, bank/4, data->od, lsb, msb); /* retime avaiable for all pins by default */ pc->rt_pin_mask = 0xff; of_property_read_u32(np, "st,retime-pin-mask", &pc->rt_pin_mask); st_pctl_dt_setup_retime(info, bank, pc); - return 0; + return; } /* @@ -1200,6 +1285,194 @@ static int st_pctl_parse_functions(struct device_node *np, return 0; } +static int st_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct st_gpio_bank *bank = gpio_chip_to_bank(chip); + int irq = -ENXIO; + + if (offset < chip->ngpio) + irq = irq_find_mapping(bank->domain, offset); + + dev_info(chip->dev, "%s: request IRQ for GPIO %d, return %d\n", + chip->label, offset + chip->base, irq); + return irq; +} + +static void st_gpio_irq_mask(struct irq_data *d) +{ + struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d); + + writel(BIT(d->hwirq), bank->base + REG_PIO_CLR_PMASK); +} + +static void st_gpio_irq_unmask(struct irq_data *d) +{ + struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d); + + writel(BIT(d->hwirq), bank->base + REG_PIO_SET_PMASK); +} + +static unsigned int st_gpio_irq_startup(struct irq_data *d) +{ + struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d); + + if (gpio_lock_as_irq(&bank->gpio_chip, d->hwirq)) + dev_err(bank->gpio_chip.dev, + "unable to lock HW IRQ %lu for IRQ\n", + d->hwirq); + + st_gpio_irq_unmask(d); + + return 0; +} + +static void st_gpio_irq_shutdown(struct irq_data *d) +{ + struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d); + + st_gpio_irq_mask(d); + gpio_unlock_as_irq(&bank->gpio_chip, d->hwirq); +} + +static int st_gpio_irq_set_type(struct irq_data *d, unsigned type) +{ + struct st_gpio_bank *bank = irq_data_get_irq_chip_data(d); + unsigned long flags; + int comp, pin = d->hwirq; + u32 val; + u32 pin_edge_conf = 0; + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + comp = 0; + break; + case IRQ_TYPE_EDGE_FALLING: + comp = 0; + pin_edge_conf = ST_IRQ_FALLING_EDGE_CONF(pin); + break; + case IRQ_TYPE_LEVEL_LOW: + comp = 1; + break; + case IRQ_TYPE_EDGE_RISING: + comp = 1; + pin_edge_conf = ST_IRQ_RISING_EDGE_CONF(pin); + break; + case IRQ_TYPE_EDGE_BOTH: + comp = st_gpio_get(&bank->gpio_chip, pin); + pin_edge_conf = ST_IRQ_BOTH_EDGE_CONF(pin); + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&bank->lock, flags); + bank->irq_edge_conf &= ~(ST_IRQ_EDGE_MASK << ( + pin * ST_IRQ_EDGE_CONF_BITS_PER_PIN)); + bank->irq_edge_conf |= pin_edge_conf; + spin_unlock_irqrestore(&bank->lock, flags); + + val = readl(bank->base + REG_PIO_PCOMP); + val &= ~BIT(pin); + val |= (comp << pin); + writel(val, bank->base + REG_PIO_PCOMP); + + return 0; +} + +/* + * As edge triggers are not supported at hardware level, it is supported by + * software by exploiting the level trigger support in hardware. + * + * Steps for detection raising edge interrupt in software. + * + * Step 1: CONFIGURE pin to detect level LOW interrupts. + * + * Step 2: DETECT level LOW interrupt and in irqmux/gpio bank interrupt handler, + * if the value of pin is low, then CONFIGURE pin for level HIGH interrupt. + * IGNORE calling the actual interrupt handler for the pin at this stage. + * + * Step 3: DETECT level HIGH interrupt and in irqmux/gpio-bank interrupt handler + * if the value of pin is HIGH, CONFIGURE pin for level LOW interrupt and then + * DISPATCH the interrupt to the interrupt handler of the pin. + * + * step-1 ________ __________ + * | | step - 3 + * | | + * step -2 |_____| + * + * falling edge is also detected int the same way. + * + */ +static void __gpio_irq_handler(struct st_gpio_bank *bank) +{ + unsigned long port_in, port_mask, port_comp, active_irqs; + unsigned long bank_edge_mask, flags; + int n, val, ecfg; + + spin_lock_irqsave(&bank->lock, flags); + bank_edge_mask = bank->irq_edge_conf; + spin_unlock_irqrestore(&bank->lock, flags); + + for (;;) { + port_in = readl(bank->base + REG_PIO_PIN); + port_comp = readl(bank->base + REG_PIO_PCOMP); + port_mask = readl(bank->base + REG_PIO_PMASK); + + active_irqs = (port_in ^ port_comp) & port_mask; + + if (active_irqs == 0) + break; + + for_each_set_bit(n, &active_irqs, BITS_PER_LONG) { + /* check if we are detecting fake edges ... */ + ecfg = ST_IRQ_EDGE_CONF(bank_edge_mask, n); + + if (ecfg) { + /* edge detection. */ + val = st_gpio_get(&bank->gpio_chip, n); + + writel(BIT(n), + val ? bank->base + REG_PIO_SET_PCOMP : + bank->base + REG_PIO_CLR_PCOMP); + + if (ecfg != ST_IRQ_EDGE_BOTH && + !((ecfg & ST_IRQ_EDGE_FALLING) ^ val)) + continue; + } + + generic_handle_irq(irq_find_mapping(bank->domain, n)); + } + } +} + +static void st_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + /* interrupt dedicated per bank */ + struct irq_chip *chip = irq_get_chip(irq); + struct st_gpio_bank *bank = irq_get_handler_data(irq); + + chained_irq_enter(chip, desc); + __gpio_irq_handler(bank); + chained_irq_exit(chip, desc); +} + +static void st_gpio_irqmux_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct st_pinctrl *info = irq_get_handler_data(irq); + unsigned long status; + int n; + + chained_irq_enter(chip, desc); + + status = readl(info->irqmux_base); + + for_each_set_bit(n, &status, ST_GPIO_PINS_PER_BANK) + __gpio_irq_handler(&info->banks[n]); + + chained_irq_exit(chip, desc); +} + static struct gpio_chip st_gpio_template = { .request = st_gpio_request, .free = st_gpio_free, @@ -1210,6 +1483,34 @@ static struct gpio_chip st_gpio_template = { .ngpio = ST_GPIO_PINS_PER_BANK, .of_gpio_n_cells = 1, .of_xlate = st_gpio_xlate, + .to_irq = st_gpio_to_irq, +}; + +static struct irq_chip st_gpio_irqchip = { + .name = "GPIO", + .irq_mask = st_gpio_irq_mask, + .irq_unmask = st_gpio_irq_unmask, + .irq_set_type = st_gpio_irq_set_type, + .irq_startup = st_gpio_irq_startup, + .irq_shutdown = st_gpio_irq_shutdown, +}; + +static int st_gpio_irq_domain_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hw) +{ + struct st_gpio_bank *bank = h->host_data; + + irq_set_chip(virq, &st_gpio_irqchip); + irq_set_handler(virq, handle_simple_irq); + set_irq_flags(virq, IRQF_VALID); + irq_set_chip_data(virq, bank); + + return 0; +} + +static struct irq_domain_ops st_gpio_irq_ops = { + .map = st_gpio_irq_domain_map, + .xlate = irq_domain_xlate_twocell, }; static int st_gpiolib_register_bank(struct st_pinctrl *info, @@ -1219,8 +1520,8 @@ static int st_gpiolib_register_bank(struct st_pinctrl *info, struct pinctrl_gpio_range *range = &bank->range; struct device *dev = info->dev; int bank_num = of_alias_get_id(np, "gpio"); - struct resource res; - int err; + struct resource res, irq_res; + int gpio_irq = 0, err, i; if (of_address_to_resource(np, 0, &res)) return -ENODEV; @@ -1233,6 +1534,7 @@ static int st_gpiolib_register_bank(struct st_pinctrl *info, bank->gpio_chip.base = bank_num * ST_GPIO_PINS_PER_BANK; bank->gpio_chip.ngpio = ST_GPIO_PINS_PER_BANK; bank->gpio_chip.of_node = np; + spin_lock_init(&bank->lock); of_property_read_string(np, "st,bank-name", &range->name); bank->gpio_chip.label = range->name; @@ -1248,6 +1550,51 @@ static int st_gpiolib_register_bank(struct st_pinctrl *info, } dev_info(dev, "%s bank added.\n", range->name); + /** + * GPIO bank can have one of the two possible types of + * interrupt-wirings. + * + * First type is via irqmux, single interrupt is used by multiple + * gpio banks. This reduces number of overall interrupts numbers + * required. All these banks belong to a single pincontroller. + * _________ + * | |----> [gpio-bank (n) ] + * | |----> [gpio-bank (n + 1)] + * [irqN]-- | irq-mux |----> [gpio-bank (n + 2)] + * | |----> [gpio-bank (... )] + * |_________|----> [gpio-bank (n + 7)] + * + * Second type has a dedicated interrupt per each gpio bank. + * + * [irqN]----> [gpio-bank (n)] + */ + + if (of_irq_to_resource(np, 0, &irq_res)) { + gpio_irq = irq_res.start; + irq_set_chained_handler(gpio_irq, st_gpio_irq_handler); + irq_set_handler_data(gpio_irq, bank); + } + + if (info->irqmux_base > 0 || gpio_irq > 0) { + /* Setup IRQ domain */ + bank->domain = irq_domain_add_linear(np, + ST_GPIO_PINS_PER_BANK, + &st_gpio_irq_ops, bank); + if (!bank->domain) { + dev_err(dev, "Failed to add irq domain for %s\n", + np->full_name); + } else { + for (i = 0; i < ST_GPIO_PINS_PER_BANK; i++) { + if (irq_create_mapping(bank->domain, i) < 0) + dev_err(dev, + "Failed to map IRQ %i\n", i); + } + } + + } else { + dev_info(dev, "No IRQ support for %s bank\n", np->full_name); + } + return 0; } @@ -1264,6 +1611,10 @@ static struct of_device_id st_pctl_of_match[] = { { .compatible = "st,stih416-rear-pinctrl", .data = &stih416_data}, { .compatible = "st,stih416-fvdp-fe-pinctrl", .data = &stih416_data}, { .compatible = "st,stih416-fvdp-lite-pinctrl", .data = &stih416_data}, + { .compatible = "st,stih407-sbc-pinctrl", .data = &stih416_data}, + { .compatible = "st,stih407-front-pinctrl", .data = &stih416_data}, + { .compatible = "st,stih407-rear-pinctrl", .data = &stih416_data}, + { .compatible = "st,stih407-flash-pinctrl", .data = &stih407_flashdata}, { /* sentinel */ } }; @@ -1276,6 +1627,8 @@ static int st_pctl_probe_dt(struct platform_device *pdev, struct device_node *np = pdev->dev.of_node; struct device_node *child; int grp_index = 0; + int irq = 0; + struct resource *res; st_pctl_dt_child_count(info, np); if (!info->nbanks) { @@ -1306,6 +1659,21 @@ static int st_pctl_probe_dt(struct platform_device *pdev, } info->data = of_match_node(st_pctl_of_match, np)->data; + irq = platform_get_irq(pdev, 0); + + if (irq > 0) { + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "irqmux"); + info->irqmux_base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(info->irqmux_base)) + return PTR_ERR(info->irqmux_base); + + irq_set_chained_handler(irq, st_gpio_irqmux_handler); + irq_set_handler_data(irq, info); + + } + pctl_desc->npins = info->nbanks * ST_GPIO_PINS_PER_BANK; pdesc = devm_kzalloc(&pdev->dev, sizeof(*pdesc) * pctl_desc->npins, GFP_KERNEL); diff --git a/drivers/pinctrl/pinctrl-sunxi-pins.h b/drivers/pinctrl/pinctrl-sunxi-pins.h index 6fd8d4d95140..3d6066988a72 100644 --- a/drivers/pinctrl/pinctrl-sunxi-pins.h +++ b/drivers/pinctrl/pinctrl-sunxi-pins.h @@ -1932,27 +1932,27 @@ static const struct sunxi_desc_pin sun5i_a13_pins[] = { SUNXI_PIN(SUNXI_PINCTRL_PIN_PF0, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* D1 */ + SUNXI_FUNCTION(0x2, "mmc0")), /* D1 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF1, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* D0 */ + SUNXI_FUNCTION(0x2, "mmc0")), /* D0 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF2, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* CLK */ + SUNXI_FUNCTION(0x2, "mmc0")), /* CLK */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF3, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* CMD */ + SUNXI_FUNCTION(0x2, "mmc0")), /* CMD */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF4, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* D3 */ + SUNXI_FUNCTION(0x2, "mmc0")), /* D3 */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PF5, SUNXI_FUNCTION(0x0, "gpio_in"), SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* D2 */ + SUNXI_FUNCTION(0x2, "mmc0")), /* D2 */ /* Hole */ SUNXI_PIN(SUNXI_PINCTRL_PIN_PG0, SUNXI_FUNCTION(0x0, "gpio_in"), diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c index 9ccf681dad2f..f9fabe9bf47d 100644 --- a/drivers/pinctrl/pinctrl-sunxi.c +++ b/drivers/pinctrl/pinctrl-sunxi.c @@ -14,6 +14,7 @@ #include <linux/clk.h> #include <linux/gpio.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> @@ -584,7 +585,7 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, spin_lock_irqsave(&pctl->lock, flags); regval = readl(pctl->membase + reg); - regval &= ~IRQ_CFG_IRQ_MASK; + regval &= ~(IRQ_CFG_IRQ_MASK << index); writel(regval | (mode << index), pctl->membase + reg); spin_unlock_irqrestore(&pctl->lock, flags); @@ -665,6 +666,7 @@ static struct irq_chip sunxi_pinctrl_irq_chip = { static void sunxi_pinctrl_irq_handler(unsigned irq, struct irq_desc *desc) { + struct irq_chip *chip = irq_get_chip(irq); struct sunxi_pinctrl *pctl = irq_get_handler_data(irq); const unsigned long reg = readl(pctl->membase + IRQ_STATUS_REG); @@ -674,10 +676,12 @@ static void sunxi_pinctrl_irq_handler(unsigned irq, struct irq_desc *desc) if (reg) { int irqoffset; + chained_irq_enter(chip, desc); for_each_set_bit(irqoffset, ®, SUNXI_IRQ_NUMBER) { int pin_irq = irq_find_mapping(pctl->domain, irqoffset); generic_handle_irq(pin_irq); } + chained_irq_exit(chip, desc); } } diff --git a/drivers/pinctrl/pinctrl-sunxi.h b/drivers/pinctrl/pinctrl-sunxi.h index 01c494f8a14f..552b0e97077a 100644 --- a/drivers/pinctrl/pinctrl-sunxi.h +++ b/drivers/pinctrl/pinctrl-sunxi.h @@ -511,7 +511,7 @@ static inline u32 sunxi_pull_offset(u16 pin) static inline u32 sunxi_irq_cfg_reg(u16 irq) { - u8 reg = irq / IRQ_CFG_IRQ_PER_REG; + u8 reg = irq / IRQ_CFG_IRQ_PER_REG * 0x04; return reg + IRQ_CFG_REG; } @@ -523,7 +523,7 @@ static inline u32 sunxi_irq_cfg_offset(u16 irq) static inline u32 sunxi_irq_ctrl_reg(u16 irq) { - u8 reg = irq / IRQ_CTRL_IRQ_PER_REG; + u8 reg = irq / IRQ_CTRL_IRQ_PER_REG * 0x04; return reg + IRQ_CTRL_REG; } @@ -535,7 +535,7 @@ static inline u32 sunxi_irq_ctrl_offset(u16 irq) static inline u32 sunxi_irq_status_reg(u16 irq) { - u8 reg = irq / IRQ_STATUS_IRQ_PER_REG; + u8 reg = irq / IRQ_STATUS_IRQ_PER_REG * 0x04; return reg + IRQ_STATUS_REG; } diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index e767355ab0ad..65458096f41e 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -39,6 +39,7 @@ struct tegra_pmx { struct pinctrl_dev *pctl; const struct tegra_pinctrl_soc_data *soc; + const char **group_pins; int nbanks; void __iomem **regs; @@ -620,6 +621,8 @@ int tegra_pinctrl_probe(struct platform_device *pdev, struct tegra_pmx *pmx; struct resource *res; int i; + const char **group_pins; + int fn, gn, gfn; pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); if (!pmx) { @@ -629,6 +632,41 @@ int tegra_pinctrl_probe(struct platform_device *pdev, pmx->dev = &pdev->dev; pmx->soc = soc_data; + /* + * Each mux group will appear in 4 functions' list of groups. + * This over-allocates slightly, since not all groups are mux groups. + */ + pmx->group_pins = devm_kzalloc(&pdev->dev, + soc_data->ngroups * 4 * sizeof(*pmx->group_pins), + GFP_KERNEL); + if (!pmx->group_pins) + return -ENOMEM; + + group_pins = pmx->group_pins; + for (fn = 0; fn < soc_data->nfunctions; fn++) { + struct tegra_function *func = &soc_data->functions[fn]; + + func->groups = group_pins; + + for (gn = 0; gn < soc_data->ngroups; gn++) { + const struct tegra_pingroup *g = &soc_data->groups[gn]; + + if (g->mux_reg == -1) + continue; + + for (gfn = 0; gfn < 4; gfn++) + if (g->funcs[gfn] == fn) + break; + if (gfn == 4) + continue; + + BUG_ON(group_pins - pmx->group_pins >= + soc_data->ngroups * 4); + *group_pins++ = g->name; + func->ngroups++; + } + } + tegra_pinctrl_gpio_range.npins = pmx->soc->ngpios; tegra_pinctrl_desc.name = dev_name(&pdev->dev); tegra_pinctrl_desc.pins = pmx->soc->pins; diff --git a/drivers/pinctrl/pinctrl-tegra.h b/drivers/pinctrl/pinctrl-tegra.h index 817f7061dc4c..6053832d433e 100644 --- a/drivers/pinctrl/pinctrl-tegra.h +++ b/drivers/pinctrl/pinctrl-tegra.h @@ -72,7 +72,7 @@ enum tegra_pinconf_tristate { */ struct tegra_function { const char *name; - const char * const *groups; + const char **groups; unsigned ngroups; }; @@ -193,7 +193,7 @@ struct tegra_pinctrl_soc_data { unsigned ngpios; const struct pinctrl_pin_desc *pins; unsigned npins; - const struct tegra_function *functions; + struct tegra_function *functions; unsigned nfunctions; const struct tegra_pingroup *groups; unsigned ngroups; diff --git a/drivers/pinctrl/pinctrl-tegra114.c b/drivers/pinctrl/pinctrl-tegra114.c index 93c9e3899d5e..63fe7619d3ff 100644 --- a/drivers/pinctrl/pinctrl-tegra114.c +++ b/drivers/pinctrl/pinctrl-tegra114.c @@ -1,10 +1,8 @@ /* - * Pinctrl data and driver for the NVIDIA Tegra114 pinmux + * Pinctrl data for the NVIDIA Tegra114 pinmux * * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. * - * Author: Pritesh Raithatha <praithatha@nvidia.com> - * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. @@ -13,9 +11,6 @@ * 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> @@ -203,8 +198,8 @@ #define TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5 _GPIO(245) /* All non-GPIO pins follow */ -#define NUM_GPIOS (TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5 + 1) -#define _PIN(offset) (NUM_GPIOS + (offset)) +#define NUM_GPIOS (TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5 + 1) +#define _PIN(offset) (NUM_GPIOS + (offset)) /* Non-GPIO pins */ #define TEGRA_PIN_CORE_PWR_REQ _PIN(0) @@ -212,8 +207,11 @@ #define TEGRA_PIN_PWR_INT_N _PIN(2) #define TEGRA_PIN_RESET_OUT_N _PIN(3) #define TEGRA_PIN_OWR _PIN(4) +#define TEGRA_PIN_JTAG_RTCK _PIN(5) +#define TEGRA_PIN_CLK_32K_IN _PIN(6) +#define TEGRA_PIN_GMI_CLK_LB _PIN(7) -static const struct pinctrl_pin_desc tegra114_pins[] = { +static const struct pinctrl_pin_desc tegra114_pins[] = { PINCTRL_PIN(TEGRA_PIN_CLK_32K_OUT_PA0, "CLK_32K_OUT PA0"), PINCTRL_PIN(TEGRA_PIN_UART3_CTS_N_PA1, "UART3_CTS_N PA1"), PINCTRL_PIN(TEGRA_PIN_DAP2_FS_PA2, "DAP2_FS PA2"), @@ -385,9 +383,12 @@ static const struct pinctrl_pin_desc tegra114_pins[] = { PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5, "SDMMC3_CLK_LB_IN PEE5"), PINCTRL_PIN(TEGRA_PIN_CORE_PWR_REQ, "CORE_PWR_REQ"), PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ, "CPU_PWR_REQ"), - PINCTRL_PIN(TEGRA_PIN_OWR, "OWR"), PINCTRL_PIN(TEGRA_PIN_PWR_INT_N, "PWR_INT_N"), PINCTRL_PIN(TEGRA_PIN_RESET_OUT_N, "RESET_OUT_N"), + PINCTRL_PIN(TEGRA_PIN_OWR, "OWR"), + PINCTRL_PIN(TEGRA_PIN_JTAG_RTCK, "JTAG_RTCK"), + PINCTRL_PIN(TEGRA_PIN_CLK_32K_IN, "CLK_32K_IN"), + PINCTRL_PIN(TEGRA_PIN_GMI_CLK_LB, "GMI_CLK_LB"), }; static const unsigned clk_32k_out_pa0_pins[] = { @@ -1074,10 +1075,6 @@ static const unsigned cpu_pwr_req_pins[] = { TEGRA_PIN_CPU_PWR_REQ, }; -static const unsigned owr_pins[] = { - TEGRA_PIN_OWR, -}; - static const unsigned pwr_int_n_pins[] = { TEGRA_PIN_PWR_INT_N, }; @@ -1086,6 +1083,22 @@ static const unsigned reset_out_n_pins[] = { TEGRA_PIN_RESET_OUT_N, }; +static const unsigned owr_pins[] = { + TEGRA_PIN_OWR, +}; + +static const unsigned jtag_rtck_pins[] = { + TEGRA_PIN_JTAG_RTCK, +}; + +static const unsigned clk_32k_in_pins[] = { + TEGRA_PIN_CLK_32K_IN, +}; + +static const unsigned gmi_clk_lb_pins[] = { + TEGRA_PIN_GMI_CLK_LB, +}; + static const unsigned drive_ao1_pins[] = { TEGRA_PIN_KB_ROW0_PR0, TEGRA_PIN_KB_ROW1_PR1, @@ -1127,7 +1140,6 @@ static const unsigned drive_at1_pins[] = { TEGRA_PIN_GMI_AD13_PH5, TEGRA_PIN_GMI_AD14_PH6, TEGRA_PIN_GMI_AD15_PH7, - TEGRA_PIN_GMI_IORDY_PI5, TEGRA_PIN_GMI_CS7_N_PI6, }; @@ -1141,15 +1153,12 @@ static const unsigned drive_at2_pins[] = { TEGRA_PIN_GMI_AD5_PG5, TEGRA_PIN_GMI_AD6_PG6, TEGRA_PIN_GMI_AD7_PG7, - TEGRA_PIN_GMI_WR_N_PI0, TEGRA_PIN_GMI_OE_N_PI1, TEGRA_PIN_GMI_CS6_N_PI3, TEGRA_PIN_GMI_RST_N_PI4, TEGRA_PIN_GMI_WAIT_PI7, - TEGRA_PIN_GMI_DQS_P_PJ3, - TEGRA_PIN_GMI_ADV_N_PK0, TEGRA_PIN_GMI_CLK_PK1, TEGRA_PIN_GMI_CS4_N_PK2, @@ -1342,14 +1351,37 @@ static const unsigned drive_uda_pins[] = { }; static const unsigned drive_dev3_pins[] = { - TEGRA_PIN_CLK3_OUT_PEE0, - TEGRA_PIN_CLK3_REQ_PEE1, +}; + +static const unsigned drive_cec_pins[] = { +}; + +static const unsigned drive_at6_pins[] = { +}; + +static const unsigned drive_dap5_pins[] = { +}; + +static const unsigned drive_usb_vbus_en_pins[] = { +}; + +static const unsigned drive_ao3_pins[] = { +}; + +static const unsigned drive_hv0_pins[] = { +}; + +static const unsigned drive_sdio4_pins[] = { +}; + +static const unsigned drive_ao0_pins[] = { }; enum tegra_mux { TEGRA_MUX_BLINK, TEGRA_MUX_CEC, TEGRA_MUX_CLDVFS, + TEGRA_MUX_CLK, TEGRA_MUX_CLK12, TEGRA_MUX_CPU, TEGRA_MUX_DAP, @@ -1394,6 +1426,7 @@ enum tegra_mux { TEGRA_MUX_RSVD2, TEGRA_MUX_RSVD3, TEGRA_MUX_RSVD4, + TEGRA_MUX_RTCK, TEGRA_MUX_SDMMC1, TEGRA_MUX_SDMMC2, TEGRA_MUX_SDMMC3, @@ -1425,944 +1458,16 @@ enum tegra_mux { TEGRA_MUX_VI_ALT3, }; -static const char * const blink_groups[] = { - "clk_32k_out_pa0", -}; - -static const char * const cec_groups[] = { - "hdmi_cec_pee3", -}; - -static const char * const cldvfs_groups[] = { - "gmi_ad9_ph1", - "gmi_ad10_ph2", - "kb_row7_pr7", - "kb_row8_ps0", - "dvfs_pwm_px0", - "dvfs_clk_px2", -}; - -static const char * const clk12_groups[] = { - "sdmmc1_wp_n_pv3", - "sdmmc1_clk_pz0", -}; - -static const char * const cpu_groups[] = { - "cpu_pwr_req", -}; - -static const char * const dap_groups[] = { - "clk1_req_pee2", - "clk2_req_pcc5", -}; - -static const char * const dap1_groups[] = { - "clk1_req_pee2", -}; - -static const char * const dap2_groups[] = { - "clk1_out_pw4", - "gpio_x4_aud_px4", -}; - -static const char * const dev3_groups[] = { - "clk3_req_pee1", -}; - -static const char * const displaya_groups[] = { - "dap3_fs_pp0", - "dap3_din_pp1", - "dap3_dout_pp2", - "dap3_sclk_pp3", - "uart3_rts_n_pc0", - "pu3", - "pu4", - "pu5", - "pbb3", - "pbb4", - "pbb5", - "pbb6", - "kb_row3_pr3", - "kb_row4_pr4", - "kb_row5_pr5", - "kb_row6_pr6", - "kb_col3_pq3", - "sdmmc3_dat2_pb5", -}; - -static const char * const displaya_alt_groups[] = { - "kb_row6_pr6", -}; - -static const char * const displayb_groups[] = { - "dap3_fs_pp0", - "dap3_din_pp1", - "dap3_dout_pp2", - "dap3_sclk_pp3", - "pu3", - "pu4", - "pu5", - "pu6", - "pbb3", - "pbb4", - "pbb5", - "pbb6", - "kb_row3_pr3", - "kb_row4_pr4", - "kb_row5_pr5", - "kb_row6_pr6", - "sdmmc3_dat3_pb4", -}; - -static const char * const dtv_groups[] = { - "uart3_cts_n_pa1", - "uart3_rts_n_pc0", - "dap4_fs_pp4", - "dap4_dout_pp6", - "gmi_wait_pi7", - "gmi_ad8_ph0", - "gmi_ad14_ph6", - "gmi_ad15_ph7", -}; - -static const char * const emc_dll_groups[] = { - "kb_col0_pq0", - "kb_col1_pq1", -}; - -static const char * const extperiph1_groups[] = { - "clk1_out_pw4", -}; - -static const char * const extperiph2_groups[] = { - "clk2_out_pw5", -}; - -static const char * const extperiph3_groups[] = { - "clk3_out_pee0", -}; - -static const char * const gmi_groups[] = { - "gmi_wp_n_pc7", - - "gmi_ad0_pg0", - "gmi_ad1_pg1", - "gmi_ad2_pg2", - "gmi_ad3_pg3", - "gmi_ad4_pg4", - "gmi_ad5_pg5", - "gmi_ad6_pg6", - "gmi_ad7_pg7", - "gmi_ad8_ph0", - "gmi_ad9_ph1", - "gmi_ad10_ph2", - "gmi_ad11_ph3", - "gmi_ad12_ph4", - "gmi_ad13_ph5", - "gmi_ad14_ph6", - "gmi_ad15_ph7", - "gmi_wr_n_pi0", - "gmi_oe_n_pi1", - "gmi_cs6_n_pi3", - "gmi_rst_n_pi4", - "gmi_iordy_pi5", - "gmi_cs7_n_pi6", - "gmi_wait_pi7", - "gmi_cs0_n_pj0", - "gmi_cs1_n_pj2", - "gmi_dqs_p_pj3", - "gmi_adv_n_pk0", - "gmi_clk_pk1", - "gmi_cs4_n_pk2", - "gmi_cs2_n_pk3", - "gmi_cs3_n_pk4", - "gmi_a16_pj7", - "gmi_a17_pb0", - "gmi_a18_pb1", - "gmi_a19_pk7", - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "sdmmc4_dat4_paa4", - "sdmmc4_dat5_paa5", - "sdmmc4_dat6_paa6", - "sdmmc4_dat7_paa7", - "sdmmc4_clk_pcc4", - "sdmmc4_cmd_pt7", - "dap1_fs_pn0", - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_sclk_pn3", -}; - -static const char * const gmi_alt_groups[] = { - "gmi_wp_n_pc7", - "gmi_cs3_n_pk4", - "gmi_a16_pj7", -}; - -static const char * const hda_groups[] = { - "dap1_fs_pn0", - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_sclk_pn3", - "dap2_fs_pa2", - "dap2_sclk_pa3", - "dap2_din_pa4", - "dap2_dout_pa5", -}; - -static const char * const hsi_groups[] = { - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", -}; - -static const char * const i2c1_groups[] = { - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - "gpio_w2_aud_pw2", - "gpio_w3_aud_pw3", -}; - -static const char * const i2c2_groups[] = { - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", -}; - -static const char * const i2c3_groups[] = { - "cam_i2c_scl_pbb1", - "cam_i2c_sda_pbb2", -}; - -static const char * const i2c4_groups[] = { - "ddc_scl_pv4", - "ddc_sda_pv5", -}; - -static const char * const i2cpwr_groups[] = { - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", -}; - -static const char * const i2s0_groups[] = { - "dap1_fs_pn0", - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_sclk_pn3", -}; - -static const char * const i2s1_groups[] = { - "dap2_fs_pa2", - "dap2_sclk_pa3", - "dap2_din_pa4", - "dap2_dout_pa5", -}; - -static const char * const i2s2_groups[] = { - "dap3_fs_pp0", - "dap3_din_pp1", - "dap3_dout_pp2", - "dap3_sclk_pp3", -}; - -static const char * const i2s3_groups[] = { - "dap4_fs_pp4", - "dap4_din_pp5", - "dap4_dout_pp6", - "dap4_sclk_pp7", -}; - -static const char * const i2s4_groups[] = { - "pcc1", - "pbb0", - "pbb7", - "pcc2", -}; - -static const char * const irda_groups[] = { - "uart2_rxd_pc3", - "uart2_txd_pc2", -}; - -static const char * const kbc_groups[] = { - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row2_pr2", - "kb_row3_pr3", - "kb_row4_pr4", - "kb_row5_pr5", - "kb_row6_pr6", - "kb_row7_pr7", - "kb_row8_ps0", - "kb_row9_ps1", - "kb_row10_ps2", - "kb_col0_pq0", - "kb_col1_pq1", - "kb_col2_pq2", - "kb_col3_pq3", - "kb_col4_pq4", - "kb_col5_pq5", - "kb_col6_pq6", - "kb_col7_pq7", -}; - -static const char * const nand_groups[] = { - "gmi_wp_n_pc7", - "gmi_wait_pi7", - "gmi_adv_n_pk0", - "gmi_clk_pk1", - "gmi_cs0_n_pj0", - "gmi_cs1_n_pj2", - "gmi_cs2_n_pk3", - "gmi_cs3_n_pk4", - "gmi_cs4_n_pk2", - "gmi_cs6_n_pi3", - "gmi_cs7_n_pi6", - "gmi_ad0_pg0", - "gmi_ad1_pg1", - "gmi_ad2_pg2", - "gmi_ad3_pg3", - "gmi_ad4_pg4", - "gmi_ad5_pg5", - "gmi_ad6_pg6", - "gmi_ad7_pg7", - "gmi_ad8_ph0", - "gmi_ad9_ph1", - "gmi_ad10_ph2", - "gmi_ad11_ph3", - "gmi_ad12_ph4", - "gmi_ad13_ph5", - "gmi_ad14_ph6", - "gmi_ad15_ph7", - "gmi_wr_n_pi0", - "gmi_oe_n_pi1", - "gmi_dqs_p_pj3", - "gmi_rst_n_pi4", -}; - -static const char * const nand_alt_groups[] = { - "gmi_cs6_n_pi3", - "gmi_cs7_n_pi6", - "gmi_rst_n_pi4", -}; - -static const char * const owr_groups[] = { - "pu0", - "kb_col4_pq4", - "owr", - "sdmmc3_cd_n_pv2", -}; - -static const char * const pmi_groups[] = { - "pwr_int_n", -}; - -static const char * const pwm0_groups[] = { - "sdmmc1_dat2_py5", - "uart3_rts_n_pc0", - "pu3", - "gmi_ad8_ph0", - "sdmmc3_dat3_pb4", -}; - -static const char * const pwm1_groups[] = { - "sdmmc1_dat1_py6", - "pu4", - "gmi_ad9_ph1", - "sdmmc3_dat2_pb5", -}; - -static const char * const pwm2_groups[] = { - "pu5", - "gmi_ad10_ph2", - "kb_col3_pq3", - "sdmmc3_dat1_pb6", -}; - -static const char * const pwm3_groups[] = { - "pu6", - "gmi_ad11_ph3", - "sdmmc3_cmd_pa7", -}; - -static const char * const pwron_groups[] = { - "core_pwr_req", -}; - -static const char * const reset_out_n_groups[] = { - "reset_out_n", -}; - -static const char * const rsvd1_groups[] = { - "pv1", - "hdmi_int_pn7", - "pu1", - "pu2", - "gmi_wp_n_pc7", - "gmi_adv_n_pk0", - "gmi_cs0_n_pj0", - "gmi_cs1_n_pj2", - "gmi_ad0_pg0", - "gmi_ad1_pg1", - "gmi_ad2_pg2", - "gmi_ad3_pg3", - "gmi_ad4_pg4", - "gmi_ad5_pg5", - "gmi_ad6_pg6", - "gmi_ad7_pg7", - "gmi_wr_n_pi0", - "gmi_oe_n_pi1", - "gpio_x4_aud_px4", - "gpio_x5_aud_px5", - "gpio_x7_aud_px7", - - "reset_out_n", -}; - -static const char * const rsvd2_groups[] = { - "pv0", - "pv1", - "sdmmc1_dat0_py7", - "clk2_out_pw5", - "clk2_req_pcc5", - "hdmi_int_pn7", - "ddc_scl_pv4", - "ddc_sda_pv5", - "uart3_txd_pw6", - "uart3_rxd_pw7", - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - "dap4_fs_pp4", - "dap4_din_pp5", - "dap4_dout_pp6", - "dap4_sclk_pp7", - "clk3_out_pee0", - "clk3_req_pee1", - "gmi_iordy_pi5", - "gmi_a17_pb0", - "gmi_a18_pb1", - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", - "sdmmc4_clk_pcc4", - "sdmmc4_cmd_pt7", - "sdmmc4_dat7_paa7", - "pcc1", - "pbb7", - "pcc2", - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row2_pr2", - "kb_row7_pr7", - "kb_row8_ps0", - "kb_row9_ps1", - "kb_row10_ps2", - "kb_col1_pq1", - "kb_col2_pq2", - "kb_col5_pq5", - "kb_col6_pq6", - "kb_col7_pq7", - "sys_clk_req_pz5", - "core_pwr_req", - "cpu_pwr_req", - "pwr_int_n", - "owr", - "spdif_out_pk5", - "gpio_x1_aud_px1", - "sdmmc3_clk_pa6", - "sdmmc3_dat0_pb7", - "gpio_w2_aud_pw2", - "usb_vbus_en0_pn4", - "usb_vbus_en1_pn5", - "sdmmc3_clk_lb_out_pee4", - "sdmmc3_clk_lb_in_pee5", - "reset_out_n", -}; - -static const char * const rsvd3_groups[] = { - "pv0", - "pv1", - "sdmmc1_clk_pz0", - "clk2_out_pw5", - "clk2_req_pcc5", - "hdmi_int_pn7", - "ddc_scl_pv4", - "ddc_sda_pv5", - "uart2_rts_n_pj6", - "uart2_cts_n_pj5", - "uart3_txd_pw6", - "uart3_rxd_pw7", - "pu0", - "pu1", - "pu2", - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - "dap4_din_pp5", - "dap4_sclk_pp7", - "clk3_out_pee0", - "clk3_req_pee1", - "pcc1", - "cam_i2c_scl_pbb1", - "cam_i2c_sda_pbb2", - "pbb7", - "pcc2", - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row2_pr2", - "kb_row3_pr3", - "kb_row9_ps1", - "kb_row10_ps2", - "clk_32k_out_pa0", - "sys_clk_req_pz5", - "core_pwr_req", - "cpu_pwr_req", - "pwr_int_n", - "owr", - "clk1_req_pee2", - "clk1_out_pw4", - "spdif_out_pk5", - "spdif_in_pk6", - "dap2_fs_pa2", - "dap2_sclk_pa3", - "dap2_din_pa4", - "dap2_dout_pa5", - "dvfs_pwm_px0", - "gpio_x1_aud_px1", - "gpio_x3_aud_px3", - "dvfs_clk_px2", - "sdmmc3_clk_pa6", - "sdmmc3_dat0_pb7", - "hdmi_cec_pee3", - "sdmmc3_cd_n_pv2", - "usb_vbus_en0_pn4", - "usb_vbus_en1_pn5", - "sdmmc3_clk_lb_out_pee4", - "sdmmc3_clk_lb_in_pee5", - "reset_out_n", -}; - -static const char * const rsvd4_groups[] = { - "pv0", - "pv1", - "sdmmc1_clk_pz0", - "clk2_out_pw5", - "clk2_req_pcc5", - "hdmi_int_pn7", - "ddc_scl_pv4", - "ddc_sda_pv5", - "pu0", - "pu1", - "pu2", - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - "dap4_fs_pp4", - "dap4_din_pp5", - "dap4_dout_pp6", - "dap4_sclk_pp7", - "clk3_out_pee0", - "clk3_req_pee1", - "gmi_ad0_pg0", - "gmi_ad1_pg1", - "gmi_ad2_pg2", - "gmi_ad3_pg3", - "gmi_ad4_pg4", - "gmi_ad12_ph4", - "gmi_ad13_ph5", - "gmi_rst_n_pi4", - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", - "sdmmc4_clk_pcc4", - "sdmmc4_cmd_pt7", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "sdmmc4_dat4_paa4", - "sdmmc4_dat5_paa5", - "sdmmc4_dat6_paa6", - "sdmmc4_dat7_paa7", - "cam_mclk_pcc0", - "pcc1", - "cam_i2c_scl_pbb1", - "cam_i2c_sda_pbb2", - "pbb3", - "pbb4", - "pbb5", - "pbb6", - "pbb7", - "pcc2", - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row2_pr2", - "kb_col2_pq2", - "kb_col5_pq5", - "kb_col6_pq6", - "kb_col7_pq7", - "clk_32k_out_pa0", - "sys_clk_req_pz5", - "core_pwr_req", - "cpu_pwr_req", - "pwr_int_n", - "owr", - "dap1_fs_pn0", - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_sclk_pn3", - "clk1_req_pee2", - "clk1_out_pw4", - "spdif_in_pk6", - "spdif_out_pk5", - "dap2_fs_pa2", - "dap2_sclk_pa3", - "dap2_din_pa4", - "dap2_dout_pa5", - "dvfs_pwm_px0", - "gpio_x1_aud_px1", - "gpio_x3_aud_px3", - "dvfs_clk_px2", - "gpio_x5_aud_px5", - "gpio_x6_aud_px6", - "gpio_x7_aud_px7", - "sdmmc3_cd_n_pv2", - "usb_vbus_en0_pn4", - "usb_vbus_en1_pn5", - "sdmmc3_clk_lb_in_pee5", - "sdmmc3_clk_lb_out_pee4", -}; - -static const char * const sdmmc1_groups[] = { - - "sdmmc1_clk_pz0", - "sdmmc1_cmd_pz1", - "sdmmc1_dat3_py4", - "sdmmc1_dat2_py5", - "sdmmc1_dat1_py6", - "sdmmc1_dat0_py7", - "uart3_cts_n_pa1", - "kb_col5_pq5", - "sdmmc1_wp_n_pv3", -}; - -static const char * const sdmmc2_groups[] = { - "gmi_iordy_pi5", - "gmi_clk_pk1", - "gmi_cs2_n_pk3", - "gmi_cs3_n_pk4", - "gmi_cs7_n_pi6", - "gmi_ad12_ph4", - "gmi_ad13_ph5", - "gmi_ad14_ph6", - "gmi_ad15_ph7", - "gmi_dqs_p_pj3", -}; - -static const char * const sdmmc3_groups[] = { - "kb_col4_pq4", - "sdmmc3_clk_pa6", - "sdmmc3_cmd_pa7", - "sdmmc3_dat0_pb7", - "sdmmc3_dat1_pb6", - "sdmmc3_dat2_pb5", - "sdmmc3_dat3_pb4", - "hdmi_cec_pee3", - "sdmmc3_cd_n_pv2", - "sdmmc3_clk_lb_in_pee5", - "sdmmc3_clk_lb_out_pee4", -}; - -static const char * const sdmmc4_groups[] = { - "sdmmc4_clk_pcc4", - "sdmmc4_cmd_pt7", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "sdmmc4_dat4_paa4", - "sdmmc4_dat5_paa5", - "sdmmc4_dat6_paa6", - "sdmmc4_dat7_paa7", -}; - -static const char * const soc_groups[] = { - "gmi_cs1_n_pj2", - "gmi_oe_n_pi1", - "clk_32k_out_pa0", - "hdmi_cec_pee3", -}; - -static const char * const spdif_groups[] = { - "sdmmc1_cmd_pz1", - "sdmmc1_dat3_py4", - "uart2_rxd_pc3", - "uart2_txd_pc2", - "spdif_in_pk6", - "spdif_out_pk5", -}; - -static const char * const spi1_groups[] = { - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", - "gpio_x3_aud_px3", - "gpio_x4_aud_px4", - "gpio_x5_aud_px5", - "gpio_x6_aud_px6", - "gpio_x7_aud_px7", - "gpio_w3_aud_pw3", -}; - -static const char * const spi2_groups[] = { - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", - "kb_row4_pr4", - "kb_row5_pr5", - "kb_col0_pq0", - "kb_col1_pq1", - "kb_col2_pq2", - "kb_col6_pq6", - "kb_col7_pq7", - "gpio_x4_aud_px4", - "gpio_x5_aud_px5", - "gpio_x6_aud_px6", - "gpio_x7_aud_px7", - "gpio_w2_aud_pw2", - "gpio_w3_aud_pw3", -}; - -static const char * const spi3_groups[] = { - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "sdmmc4_dat4_paa4", - "sdmmc4_dat5_paa5", - "sdmmc4_dat6_paa6", - "sdmmc3_clk_pa6", - "sdmmc3_cmd_pa7", - "sdmmc3_dat0_pb7", - "sdmmc3_dat1_pb6", - "sdmmc3_dat2_pb5", - "sdmmc3_dat3_pb4", -}; - -static const char * const spi4_groups[] = { - "sdmmc1_cmd_pz1", - "sdmmc1_dat3_py4", - "sdmmc1_dat2_py5", - "sdmmc1_dat1_py6", - "sdmmc1_dat0_py7", - "uart2_rxd_pc3", - "uart2_txd_pc2", - "uart2_rts_n_pj6", - "uart2_cts_n_pj5", - "uart3_txd_pw6", - "uart3_rxd_pw7", - "uart3_cts_n_pa1", - "gmi_wait_pi7", - "gmi_cs6_n_pi3", - "gmi_ad5_pg5", - "gmi_ad6_pg6", - "gmi_ad7_pg7", - "gmi_a19_pk7", - "gmi_wr_n_pi0", - "sdmmc1_wp_n_pv3", -}; - -static const char * const spi5_groups[] = { - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", - "dap3_fs_pp0", - "dap3_din_pp1", - "dap3_dout_pp2", - "dap3_sclk_pp3", -}; - -static const char * const spi6_groups[] = { - "dvfs_pwm_px0", - "gpio_x1_aud_px1", - "gpio_x3_aud_px3", - "dvfs_clk_px2", - "gpio_x6_aud_px6", - "gpio_w2_aud_pw2", - "gpio_w3_aud_pw3", -}; - -static const char * const sysclk_groups[] = { - "sys_clk_req_pz5", -}; - -static const char * const trace_groups[] = { - "gmi_iordy_pi5", - "gmi_adv_n_pk0", - "gmi_clk_pk1", - "gmi_cs2_n_pk3", - "gmi_cs4_n_pk2", - "gmi_a16_pj7", - "gmi_a17_pb0", - "gmi_a18_pb1", - "gmi_a19_pk7", - "gmi_dqs_p_pj3", -}; - -static const char * const uarta_groups[] = { - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", - "sdmmc1_cmd_pz1", - "sdmmc1_dat3_py4", - "sdmmc1_dat2_py5", - "sdmmc1_dat1_py6", - "sdmmc1_dat0_py7", - "uart2_rxd_pc3", - "uart2_txd_pc2", - "uart2_rts_n_pj6", - "uart2_cts_n_pj5", - "pu0", - "pu1", - "pu2", - "pu3", - "pu4", - "pu5", - "pu6", - "kb_row7_pr7", - "kb_row8_ps0", - "kb_row9_ps1", - "kb_row10_ps2", - "kb_col3_pq3", - "kb_col4_pq4", - "sdmmc3_cmd_pa7", - "sdmmc3_dat1_pb6", - "sdmmc1_wp_n_pv3", -}; - -static const char * const uartb_groups[] = { - "uart2_rts_n_pj6", - "uart2_cts_n_pj5", -}; - -static const char * const uartc_groups[] = { - "uart3_txd_pw6", - "uart3_rxd_pw7", - "uart3_cts_n_pa1", - "uart3_rts_n_pc0", -}; - -static const char * const uartd_groups[] = { - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", - "gmi_a16_pj7", - "gmi_a17_pb0", - "gmi_a18_pb1", - "gmi_a19_pk7", -}; - -static const char * const ulpi_groups[] = { - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", -}; - -static const char * const usb_groups[] = { - "pv0", - "pu6", - "gmi_cs0_n_pj0", - "gmi_cs4_n_pk2", - "gmi_ad11_ph3", - "kb_col0_pq0", - "spdif_in_pk6", - "usb_vbus_en0_pn4", - "usb_vbus_en1_pn5", -}; - -static const char * const vgp1_groups[] = { - "cam_i2c_scl_pbb1", -}; - -static const char * const vgp2_groups[] = { - "cam_i2c_sda_pbb2", -}; - -static const char * const vgp3_groups[] = { - "pbb3", -}; - -static const char * const vgp4_groups[] = { - "pbb4", -}; - -static const char * const vgp5_groups[] = { - "pbb5", -}; - -static const char * const vgp6_groups[] = { - "pbb6", -}; - -static const char * const vi_groups[] = { - "cam_mclk_pcc0", - "pbb0", -}; - -static const char * const vi_alt1_groups[] = { - "cam_mclk_pcc0", - "pbb0", -}; - -static const char * const vi_alt3_groups[] = { - "cam_mclk_pcc0", - "pbb0", -}; - #define FUNCTION(fname) \ { \ .name = #fname, \ - .groups = fname##_groups, \ - .ngroups = ARRAY_SIZE(fname##_groups), \ } -static const struct tegra_function tegra114_functions[] = { +static struct tegra_function tegra114_functions[] = { FUNCTION(blink), FUNCTION(cec), FUNCTION(cldvfs), + FUNCTION(clk), FUNCTION(clk12), FUNCTION(cpu), FUNCTION(dap), @@ -2407,6 +1512,7 @@ static const struct tegra_function tegra114_functions[] = { FUNCTION(rsvd2), FUNCTION(rsvd3), FUNCTION(rsvd4), + FUNCTION(rtck), FUNCTION(sdmmc1), FUNCTION(sdmmc2), FUNCTION(sdmmc3), @@ -2438,11 +1544,11 @@ static const struct tegra_function tegra114_functions[] = { FUNCTION(vi_alt3), }; -#define DRV_PINGROUP_REG_START 0x868 /* bank 0 */ -#define PINGROUP_REG_START 0x3000 /* bank 1 */ +#define DRV_PINGROUP_REG_A 0x868 /* bank 0 */ +#define PINGROUP_REG_A 0x3000 /* bank 1 */ -#define PINGROUP_REG_Y(r) ((r) - PINGROUP_REG_START) -#define PINGROUP_REG_N(r) -1 +#define PINGROUP_REG_Y(r) ((r) - PINGROUP_REG_A) +#define PINGROUP_REG_N(r) -1 #define PINGROUP(pg_name, f0, f1, f2, f3, f_safe, r, od, ior, rcv_sel) \ { \ @@ -2484,13 +1590,14 @@ static const struct tegra_function tegra114_functions[] = { .drvtype_reg = -1, \ } -#define DRV_PINGROUP_DVRTYPE_Y(r) ((r) - DRV_PINGROUP_REG_START) -#define DRV_PINGROUP_DVRTYPE_N(r) -1 +#define DRV_PINGROUP_REG_Y(r) ((r) - DRV_PINGROUP_REG_A) +#define DRV_PINGROUP_REG_N(r) -1 + #define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, \ - drvdn_b, drvdn_w, drvup_b, drvup_w, \ - slwr_b, slwr_w, slwf_b, slwf_w, \ - drvtype) \ + drvdn_b, drvdn_w, drvup_b, drvup_w, \ + slwr_b, slwr_w, slwf_b, slwf_w, \ + drvtype) \ { \ .name = "drive_" #pg_name, \ .pins = drive_##pg_name##_pins, \ @@ -2503,7 +1610,7 @@ static const struct tegra_function tegra114_functions[] = { .lock_reg = -1, \ .ioreset_reg = -1, \ .rcv_sel_reg = -1, \ - .drv_reg = DRV_PINGROUP_DVRTYPE_Y(r), \ + .drv_reg = DRV_PINGROUP_REG_Y(r), \ .drv_bank = 0, \ .hsm_bit = hsm_b, \ .schmitt_bit = schmitt_b, \ @@ -2516,14 +1623,13 @@ static const struct tegra_function tegra114_functions[] = { .slwr_width = slwr_w, \ .slwf_bit = slwf_b, \ .slwf_width = slwf_w, \ - .drvtype_reg = DRV_PINGROUP_DVRTYPE_##drvtype(r), \ + .drvtype_reg = DRV_PINGROUP_REG_##drvtype(r), \ .drvtype_bank = 0, \ .drvtype_bit = 6, \ } static const struct tegra_pingroup tegra114_groups[] = { /* pg_name, f0, f1, f2, f3, safe, r, od, ior, rcv_sel */ - /* FIXME: Fill in correct data in safe column */ PINGROUP(ulpi_data0_po1, SPI3, HSI, UARTA, ULPI, ULPI, 0x3000, N, N, N), PINGROUP(ulpi_data1_po2, SPI3, HSI, UARTA, ULPI, ULPI, 0x3004, N, N, N), PINGROUP(ulpi_data2_po3, SPI3, HSI, UARTA, ULPI, ULPI, 0x3008, N, N, N), @@ -2635,6 +1741,7 @@ static const struct tegra_pingroup tegra114_groups[] = { PINGROUP(pbb6, VGP6, DISPLAYA, DISPLAYB, RSVD4, RSVD4, 0x32a4, N, N, N), PINGROUP(pbb7, I2S4, RSVD2, RSVD3, RSVD4, RSVD4, 0x32a8, N, N, N), PINGROUP(pcc2, I2S4, RSVD2, RSVD3, RSVD4, RSVD4, 0x32ac, N, N, N), + PINGROUP(jtag_rtck, RTCK, RSVD2, RSVD3, RSVD4, RTCK, 0x32b0, N, N, N), PINGROUP(pwr_i2c_scl_pz6, I2CPWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b4, Y, N, N), PINGROUP(pwr_i2c_sda_pz7, I2CPWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b8, Y, N, N), PINGROUP(kb_row0_pr0, KBC, RSVD2, RSVD3, RSVD4, RSVD4, 0x32bc, N, N, N), @@ -2661,6 +1768,7 @@ static const struct tegra_pingroup tegra114_groups[] = { PINGROUP(core_pwr_req, PWRON, RSVD2, RSVD3, RSVD4, RSVD4, 0x3324, N, N, N), PINGROUP(cpu_pwr_req, CPU, RSVD2, RSVD3, RSVD4, RSVD4, 0x3328, N, N, N), PINGROUP(pwr_int_n, PMI, RSVD2, RSVD3, RSVD4, RSVD4, 0x332c, N, N, N), + PINGROUP(clk_32k_in, CLK, RSVD2, RSVD3, RSVD4, CLK, 0x3330, N, N, N), PINGROUP(owr, OWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x3334, N, N, Y), PINGROUP(dap1_fs_pn0, I2S0, HDA, GMI, RSVD4, RSVD4, 0x3338, N, N, N), PINGROUP(dap1_din_pn1, I2S0, HDA, GMI, RSVD4, RSVD4, 0x333c, N, N, N), @@ -2697,38 +1805,48 @@ static const struct tegra_pingroup tegra114_groups[] = { PINGROUP(usb_vbus_en1_pn5, USB, RSVD2, RSVD3, RSVD4, RSVD4, 0x33f8, Y, N, N), PINGROUP(sdmmc3_clk_lb_in_pee5, SDMMC3, RSVD2, RSVD3, RSVD4, RSVD4, 0x33fc, N, N, N), PINGROUP(sdmmc3_clk_lb_out_pee4, SDMMC3, RSVD2, RSVD3, RSVD4, RSVD4, 0x3400, N, N, N), + PINGROUP(gmi_clk_lb, SDMMC2, NAND, GMI, RSVD4, GMI, 0x3404, N, N, N), PINGROUP(reset_out_n, RSVD1, RSVD2, RSVD3, RESET_OUT_N, RSVD3, 0x3408, N, N, N), /* pg_name, r, hsm_b, schmitt_b, lpmd_b, drvdn_b, drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, slwf_b, slwf_w, drvtype */ - DRV_PINGROUP(ao1, 0x868, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(ao2, 0x86c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(at1, 0x870, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), - DRV_PINGROUP(at2, 0x874, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), - DRV_PINGROUP(at3, 0x878, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), - DRV_PINGROUP(at4, 0x87c, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), - DRV_PINGROUP(at5, 0x880, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(cdev1, 0x884, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(cdev2, 0x888, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(dap1, 0x890, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(dap2, 0x894, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(dap3, 0x898, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(dap4, 0x89c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(dbg, 0x8a0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(sdio3, 0x8b0, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, N), - DRV_PINGROUP(spi, 0x8b4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(uaa, 0x8b8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(uab, 0x8bc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(uart2, 0x8c0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(uart3, 0x8c4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(sdio1, 0x8ec, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, N), - DRV_PINGROUP(ddc, 0x8fc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(gma, 0x900, 2, 3, 4, 14, 5, 20, 5, 28, 2, 30, 2, Y), - DRV_PINGROUP(gme, 0x910, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(gmf, 0x914, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(gmg, 0x918, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(gmh, 0x91c, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(owr, 0x920, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), - DRV_PINGROUP(uda, 0x924, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(ao1, 0x868, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(ao2, 0x86c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(at1, 0x870, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at2, 0x874, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at3, 0x878, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at4, 0x87c, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, Y), + DRV_PINGROUP(at5, 0x880, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(cdev1, 0x884, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(cdev2, 0x888, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap1, 0x890, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap2, 0x894, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap3, 0x898, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dap4, 0x89c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dbg, 0x8a0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(sdio3, 0x8b0, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, N), + DRV_PINGROUP(spi, 0x8b4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uaa, 0x8b8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uab, 0x8bc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uart2, 0x8c0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uart3, 0x8c4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(sdio1, 0x8ec, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2, N), + DRV_PINGROUP(ddc, 0x8fc, 2, 3, -1, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gma, 0x900, 2, 3, -1, 14, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gme, 0x910, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gmf, 0x914, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gmg, 0x918, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(gmh, 0x91c, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(owr, 0x920, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(uda, 0x924, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(dev3, 0x92c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(cec, 0x938, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(at6, 0x994, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, Y), + DRV_PINGROUP(dap5, 0x998, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(usb_vbus_en, 0x99c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(ao3, 0x9a0, 2, 3, 4, 12, 5, -1, -1, 28, 2, -1, -1, N), + DRV_PINGROUP(hv0, 0x9a4, 2, 3, 4, 12, 5, -1, -1, 28, 2, -1, -1, N), + DRV_PINGROUP(sdio4, 0x9a8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), + DRV_PINGROUP(ao0, 0x9ac, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), }; static const struct tegra_pinctrl_soc_data tegra114_pinctrl = { diff --git a/drivers/pinctrl/pinctrl-tegra124.c b/drivers/pinctrl/pinctrl-tegra124.c index c20e0e1dda83..73773706755b 100644 --- a/drivers/pinctrl/pinctrl-tegra124.c +++ b/drivers/pinctrl/pinctrl-tegra124.c @@ -1,7 +1,7 @@ /* * Pinctrl data for the NVIDIA Tegra124 pinmux * - * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2013-2014, NVIDIA CORPORATION. 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, @@ -212,8 +212,8 @@ #define TEGRA_PIN_PFF2 _GPIO(250) /* All non-GPIO pins follow */ -#define NUM_GPIOS (TEGRA_PIN_PFF2 + 1) -#define _PIN(offset) (NUM_GPIOS + (offset)) +#define NUM_GPIOS (TEGRA_PIN_PFF2 + 1) +#define _PIN(offset) (NUM_GPIOS + (offset)) /* Non-GPIO pins */ #define TEGRA_PIN_CORE_PWR_REQ _PIN(0) @@ -325,13 +325,13 @@ static const struct pinctrl_pin_desc tegra124_pins[] = { PINCTRL_PIN(TEGRA_PIN_KB_ROW8_PS0, "KB_ROW8 PS0"), PINCTRL_PIN(TEGRA_PIN_KB_ROW9_PS1, "KB_ROW9 PS1"), PINCTRL_PIN(TEGRA_PIN_KB_ROW10_PS2, "KB_ROW10 PS2"), - PINCTRL_PIN(TEGRA_PIN_KB_ROW11_PS3, "KB_ROW10 PS3"), - PINCTRL_PIN(TEGRA_PIN_KB_ROW12_PS4, "KB_ROW10 PS4"), - PINCTRL_PIN(TEGRA_PIN_KB_ROW13_PS5, "KB_ROW10 PS5"), - PINCTRL_PIN(TEGRA_PIN_KB_ROW14_PS6, "KB_ROW10 PS6"), - PINCTRL_PIN(TEGRA_PIN_KB_ROW15_PS7, "KB_ROW10 PS7"), - PINCTRL_PIN(TEGRA_PIN_KB_ROW16_PT0, "KB_ROW10 PT0"), - PINCTRL_PIN(TEGRA_PIN_KB_ROW17_PT1, "KB_ROW10 PT1"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW11_PS3, "KB_ROW11 PS3"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW12_PS4, "KB_ROW12 PS4"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW13_PS5, "KB_ROW13 PS5"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW14_PS6, "KB_ROW14 PS6"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW15_PS7, "KB_ROW15 PS7"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW16_PT0, "KB_ROW16 PT0"), + PINCTRL_PIN(TEGRA_PIN_KB_ROW17_PT1, "KB_ROW17 PT1"), PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SCL_PT5, "GEN2_I2C_SCL PT5"), PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SDA_PT6, "GEN2_I2C_SDA PT6"), PINCTRL_PIN(TEGRA_PIN_SDMMC4_CMD_PT7, "SDMMC4_CMD PT7"), @@ -406,16 +406,16 @@ static const struct pinctrl_pin_desc tegra124_pins[] = { PINCTRL_PIN(TEGRA_PIN_HDMI_CEC_PEE3, "HDMI_CEC PEE3"), PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_LB_OUT_PEE4, "SDMMC3_CLK_LB_OUT PEE4"), PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5, "SDMMC3_CLK_LB_IN PEE5"), + PINCTRL_PIN(TEGRA_PIN_DP_HPD_PFF0, "DP_HPD PFF0"), + PINCTRL_PIN(TEGRA_PIN_USB_VBUS_EN2_PFF1, "USB_VBUS_EN2 PFF1"), + PINCTRL_PIN(TEGRA_PIN_PFF2, "PFF2"), PINCTRL_PIN(TEGRA_PIN_CORE_PWR_REQ, "CORE_PWR_REQ"), PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ, "CPU_PWR_REQ"), - PINCTRL_PIN(TEGRA_PIN_OWR, "OWR"), PINCTRL_PIN(TEGRA_PIN_PWR_INT_N, "PWR_INT_N"), + PINCTRL_PIN(TEGRA_PIN_GMI_CLK_LB, "GMI_CLK_LB"), PINCTRL_PIN(TEGRA_PIN_RESET_OUT_N, "RESET_OUT_N"), - PINCTRL_PIN(TEGRA_PIN_DP_HPD_PFF0, "DP_HPD PFF0"), - PINCTRL_PIN(TEGRA_PIN_USB_VBUS_EN2_PFF1, "USB_VBUS_EN2 PFF1"), - PINCTRL_PIN(TEGRA_PIN_PFF2, "PFF2"), + PINCTRL_PIN(TEGRA_PIN_OWR, "OWR"), PINCTRL_PIN(TEGRA_PIN_CLK_32K_IN, "CLK_32K_IN"), - PINCTRL_PIN(TEGRA_PIN_GMI_CLK_LB, "GMI_CLK_LB"), PINCTRL_PIN(TEGRA_PIN_JTAG_RTCK, "JTAG_RTCK"), }; @@ -1138,6 +1138,7 @@ static const unsigned sdmmc3_clk_lb_out_pee4_pins[] = { static const unsigned sdmmc3_clk_lb_in_pee5_pins[] = { TEGRA_PIN_SDMMC3_CLK_LB_IN_PEE5, }; + static const unsigned dp_hpd_pff0_pins[] = { TEGRA_PIN_DP_HPD_PFF0, }; @@ -1158,24 +1159,24 @@ static const unsigned cpu_pwr_req_pins[] = { TEGRA_PIN_CPU_PWR_REQ, }; -static const unsigned owr_pins[] = { - TEGRA_PIN_OWR, -}; - static const unsigned pwr_int_n_pins[] = { TEGRA_PIN_PWR_INT_N, }; +static const unsigned gmi_clk_lb_pins[] = { + TEGRA_PIN_GMI_CLK_LB, +}; + static const unsigned reset_out_n_pins[] = { TEGRA_PIN_RESET_OUT_N, }; -static const unsigned clk_32k_in_pins[] = { - TEGRA_PIN_CLK_32K_IN, +static const unsigned owr_pins[] = { + TEGRA_PIN_OWR, }; -static const unsigned gmi_clk_lb_pins[] = { - TEGRA_PIN_GMI_CLK_LB, +static const unsigned clk_32k_in_pins[] = { + TEGRA_PIN_CLK_32K_IN, }; static const unsigned jtag_rtck_pins[] = { @@ -1441,15 +1442,15 @@ static const unsigned drive_gpv_pins[] = { TEGRA_PIN_PFF2, }; -static const unsigned drive_cec_pins[] = { - TEGRA_PIN_HDMI_CEC_PEE3, -}; - static const unsigned drive_dev3_pins[] = { TEGRA_PIN_CLK3_OUT_PEE0, TEGRA_PIN_CLK3_REQ_PEE1, }; +static const unsigned drive_cec_pins[] = { + TEGRA_PIN_HDMI_CEC_PEE3, +}; + static const unsigned drive_at6_pins[] = { TEGRA_PIN_PK1, TEGRA_PIN_PK3, @@ -1496,8 +1497,10 @@ static const unsigned drive_ao4_pins[] = { enum tegra_mux { TEGRA_MUX_BLINK, + TEGRA_MUX_CCLA, TEGRA_MUX_CEC, TEGRA_MUX_CLDVFS, + TEGRA_MUX_CLK, TEGRA_MUX_CLK12, TEGRA_MUX_CPU, TEGRA_MUX_DAP, @@ -1507,6 +1510,7 @@ enum tegra_mux { TEGRA_MUX_DISPLAYA, TEGRA_MUX_DISPLAYA_ALT, TEGRA_MUX_DISPLAYB, + TEGRA_MUX_DP, TEGRA_MUX_DTV, TEGRA_MUX_EXTPERIPH1, TEGRA_MUX_EXTPERIPH2, @@ -1528,6 +1532,9 @@ enum tegra_mux { TEGRA_MUX_IRDA, TEGRA_MUX_KBC, TEGRA_MUX_OWR, + TEGRA_MUX_PE, + TEGRA_MUX_PE0, + TEGRA_MUX_PE1, TEGRA_MUX_PMI, TEGRA_MUX_PWM0, TEGRA_MUX_PWM1, @@ -1539,6 +1546,8 @@ enum tegra_mux { TEGRA_MUX_RSVD2, TEGRA_MUX_RSVD3, TEGRA_MUX_RSVD4, + TEGRA_MUX_RTCK, + TEGRA_MUX_SATA, TEGRA_MUX_SDMMC1, TEGRA_MUX_SDMMC2, TEGRA_MUX_SDMMC3, @@ -1551,6 +1560,8 @@ enum tegra_mux { TEGRA_MUX_SPI4, TEGRA_MUX_SPI5, TEGRA_MUX_SPI6, + TEGRA_MUX_SYS, + TEGRA_MUX_TMDS, TEGRA_MUX_TRACE, TEGRA_MUX_UARTA, TEGRA_MUX_UARTB, @@ -1569,1134 +1580,19 @@ enum tegra_mux { TEGRA_MUX_VI_ALT3, TEGRA_MUX_VIMCLK2, TEGRA_MUX_VIMCLK2_ALT, - TEGRA_MUX_SATA, - TEGRA_MUX_CCLA, - TEGRA_MUX_PE0, - TEGRA_MUX_PE, - TEGRA_MUX_PE1, - TEGRA_MUX_DP, - TEGRA_MUX_RTCK, - TEGRA_MUX_SYS, - TEGRA_MUX_CLK, - TEGRA_MUX_TMDS, -}; - -static const char * const blink_groups[] = { - "clk_32k_out_pa0", -}; - -static const char * const cec_groups[] = { - "hdmi_cec_pee3", -}; - -static const char * const cldvfs_groups[] = { - "ph2", - "ph3", - "kb_row7_pr7", - "kb_row8_ps0", - "dvfs_pwm_px0", - "dvfs_clk_px2", -}; - -static const char * const clk12_groups[] = { - "sdmmc1_wp_n_pv3", - "sdmmc1_clk_pz0", -}; - -static const char * const cpu_groups[] = { - "cpu_pwr_req", -}; - -static const char * const dap_groups[] = { - "dap_mclk1_pee2", - "clk2_req_pcc5", -}; - -static const char * const dap1_groups[] = { - "dap_mclk1_pee2", -}; - -static const char * const dap2_groups[] = { - "dap_mclk1_pw4", - "gpio_x4_aud_px4", -}; - -static const char * const dev3_groups[] = { - "clk3_req_pee1", -}; - -static const char * const displaya_groups[] = { - "dap3_fs_pp0", - "dap3_din_pp1", - "dap3_dout_pp2", - "ph1", - "pi4", - "pbb3", - "pbb4", - "pbb5", - "kb_row3_pr3", - "kb_row4_pr4", - "kb_row5_pr5", - "kb_row6_pr6", - "kb_col3_pq3", - "sdmmc3_dat2_pb5", -}; - -static const char * const displaya_alt_groups[] = { - "kb_row6_pr6", -}; - -static const char * const displayb_groups[] = { - "dap3_fs_pp0", - "dap3_din_pp1", - "dap3_sclk_pp3", - - "pu3", - "pu4", - "pu5", - - "pbb3", - "pbb4", - "pbb6", - - "kb_row3_pr3", - "kb_row4_pr4", - "kb_row5_pr5", - "kb_row6_pr6", - - "sdmmc3_dat3_pb4", -}; - -static const char * const dtv_groups[] = { - "uart3_cts_n_pa1", - "uart3_rts_n_pc0", - "dap4_fs_pp4", - "dap4_dout_pp6", - "pi7", - "ph0", - "ph6", - "ph7", -}; - -static const char * const extperiph1_groups[] = { - "dap_mclk1_pw4", -}; - -static const char * const extperiph2_groups[] = { - "clk2_out_pw5", -}; - -static const char * const extperiph3_groups[] = { - "clk3_out_pee0", -}; - -static const char * const gmi_groups[] = { - "uart2_cts_n_pj5", - "uart2_rts_n_pj6", - "uart3_txd_pw6", - "uart3_rxd_pw7", - "uart3_cts_n_pa1", - "uart3_rts_n_pc0", - - "pu0", - "pu1", - "pu2", - "pu3", - "pu4", - "pu5", - "pu6", - - "dap4_fs_pp4", - "dap4_din_pp5", - "dap4_dout_pp6", - "dap4_sclk_pp7", - - "pc7", - - "pg0", - "pg1", - "pg2", - "pg3", - "pg4", - "pg5", - "pg6", - "pg7", - - "ph0", - "ph1", - "ph2", - "ph3", - "ph4", - "ph5", - "ph6", - "ph7", - - "pi0", - "pi1", - "pi2", - "pi3", - "pi4", - "pi5", - "pi6", - "pi7", - - "pj0", - "pj2", - - "pk0", - "pk1", - "pk2", - "pk3", - "pk4", - - "pj7", - "pb0", - "pb1", - "pk7", - - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", - - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "sdmmc4_dat4_paa4", - "sdmmc4_dat6_paa6", - "sdmmc4_dat7_paa7", - "sdmmc4_clk_pcc4", - "sdmmc4_cmd_pt7", - "gmi_clk_lb", - - "dap1_fs_pn0", - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_sclk_pn3", - - "dap2_fs_pa2", - "dap2_din_pa4", - "dap2_dout_pa5", - "dap2_sclk_pa3", - - "dvfs_pwm_px0", - "dvfs_clk_px2", - "gpio_x1_aud_px1", - "gpio_x3_aud_px3", - "gpio_x4_aud_px4", - "gpio_x5_aud_px5", - "gpio_x6_aud_px6", -}; - -static const char * const gmi_alt_groups[] = { - "pc7", - "pk4", - "pj7", -}; - -static const char * const hda_groups[] = { - "dap1_fs_pn0", - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_sclk_pn3", - "dap2_fs_pa2", - "dap2_sclk_pa3", - "dap2_din_pa4", - "dap2_dout_pa5", -}; - -static const char * const hsi_groups[] = { - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", -}; - -static const char * const i2c1_groups[] = { - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - "gpio_w2_aud_pw2", - "gpio_w3_aud_pw3", -}; - -static const char * const i2c2_groups[] = { - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", -}; - -static const char * const i2c3_groups[] = { - "spdif_in_pk6", - "spdif_out_pk5", - "cam_i2c_scl_pbb1", - "cam_i2c_sda_pbb2", -}; - -static const char * const i2c4_groups[] = { - "ddc_scl_pv4", - "ddc_sda_pv5", -}; - -static const char * const i2cpwr_groups[] = { - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", -}; - -static const char * const i2s0_groups[] = { - "dap1_fs_pn0", - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_sclk_pn3", -}; - -static const char * const i2s1_groups[] = { - "dap2_fs_pa2", - "dap2_sclk_pa3", - "dap2_din_pa4", - "dap2_dout_pa5", -}; - -static const char * const i2s2_groups[] = { - "dap3_fs_pp0", - "dap3_din_pp1", - "dap3_dout_pp2", - "dap3_sclk_pp3", -}; - -static const char * const i2s3_groups[] = { - "dap4_fs_pp4", - "dap4_din_pp5", - "dap4_dout_pp6", - "dap4_sclk_pp7", -}; - -static const char * const i2s4_groups[] = { - "pcc1", - "pbb6", - "pbb7", - "pcc2", -}; - -static const char * const irda_groups[] = { - "uart2_rxd_pc3", - "uart2_txd_pc2", - "kb_row11_ps3", - "kb_row12_ps4", -}; - -static const char * const kbc_groups[] = { - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row2_pr2", - "kb_row3_pr3", - "kb_row4_pr4", - "kb_row5_pr5", - "kb_row6_pr6", - "kb_row7_pr7", - "kb_row8_ps0", - "kb_row9_ps1", - "kb_row10_ps2", - "kb_row11_ps3", - "kb_row12_ps4", - "kb_row13_ps5", - "kb_row14_ps6", - "kb_row15_ps7", - "kb_row16_pt0", - "kb_row17_pt1", - - "kb_col0_pq0", - "kb_col1_pq1", - "kb_col2_pq2", - "kb_col3_pq3", - "kb_col4_pq4", - "kb_col5_pq5", - "kb_col6_pq6", - "kb_col7_pq7", -}; - -static const char * const owr_groups[] = { - "pu0", - "kb_col4_pq4", - "owr", - "sdmmc3_cd_n_pv2", -}; - -static const char * const pmi_groups[] = { - "pwr_int_n", -}; - -static const char * const pwm0_groups[] = { - "sdmmc1_dat2_py5", - "uart3_rts_n_pc0", - "pu3", - "ph0", - "sdmmc3_dat3_pb4", -}; - -static const char * const pwm1_groups[] = { - "sdmmc1_dat1_py6", - "pu4", - "ph1", - "sdmmc3_dat2_pb5", -}; - -static const char * const pwm2_groups[] = { - "pu5", - "ph2", - "kb_col3_pq3", - "sdmmc3_dat1_pb6", -}; - -static const char * const pwm3_groups[] = { - "pu6", - "ph3", - "sdmmc3_cmd_pa7", -}; - -static const char * const pwron_groups[] = { - "core_pwr_req", -}; - -static const char * const reset_out_n_groups[] = { - "reset_out_n", -}; - -static const char * const rsvd1_groups[] = { - "pv0", - "pv1", - - "hdmi_int_pn7", - "pu1", - "pu2", - "pc7", - "pi7", - "pk0", - "pj0", - "pj2", - "pk2", - "pi3", - "pi6", - - "pg0", - "pg1", - "pg2", - "pg3", - "pg4", - "pg5", - "pg6", - "pg7", - - "pi0", - "pi1", - - "gpio_x7_aud_px7", - - "reset_out_n", -}; - -static const char * const rsvd2_groups[] = { - "pv0", - "pv1", - - "sdmmc1_dat0_py7", - "clk2_out_pw5", - "clk2_req_pcc5", - "hdmi_int_pn7", - "ddc_scl_pv4", - "ddc_sda_pv5", - - "uart3_txd_pw6", - "uart3_rxd_pw7", - - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - - "clk2_out_pee0", - "clk2_req_pee1", - "pc7", - "pi5", - "pj0", - "pj2", - - "pk4", - "pk2", - "pi3", - "pi6", - "pg0", - "pg1", - "pg5", - "pg6", - "pg7", - - "ph4", - "ph5", - "pj7", - "pb0", - "pb1", - "pk7", - "pi0", - "pi1", - - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", - "sdmmc4_clk_pcc4", - "sdmmc4_cmd_pt7", - "sdmmc4_dat7_paa7", - "pcc1", - "pbb6", - "pbb7", - "pcc2", - "jtag_rtck", - - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", - - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row2_pr2", - "kb_row7_pr7", - "kb_row8_ps0", - "kb_row9_ps1", - "kb_row10_ps2", - "kb_row11_ps3", - "kb_row12_ps4", - "kb_row13_ps5", - "kb_row14_ps6", - - "kb_col0_pq0", - "kb_col1_pq1", - "kb_col2_pq2", - "kb_col5_pq5", - "kb_col6_pq6", - "kb_col7_pq7", - - "core_pwr_req", - "cpu_pwr_req", - "pwr_int_n", - "clk_32k_in", - "owr", - - "spdif_in_pk6", - "spdif_out_pk5", - "gpio_x1_aud_px1", - - "sdmmc3_clk_pa6", - "sdmmc3_dat0_pb7", - - "pex_l0_rst_n_pdd1", - "pex_l0_clkreq_n_pdd2", - "pex_wake_n_pdd3", - "pex_l1_rst_n_pdd5", - "pex_l1_clkreq_n_pdd6", - "hdmi_cec_pee3", - - "gpio_w2_aud_pw2", - "usb_vbus_en0_pn4", - "usb_vbus_en1_pn5", - "sdmmc3_clk_lb_out_pee4", - "sdmmc3_clk_lb_in_pee5", - "gmi_clk_lb", - "reset_out_n", - "kb_row16_pt0", - "kb_row17_pt1", - "dp_hpd_pff0", - "usb_vbus_en2_pff1", - "pff2", -}; - -static const char * const rsvd3_groups[] = { - "dap3_sclk_pp3", - "pv0", - "pv1", - "sdmmc1_clk_pz0", - "clk2_out_pw5", - "clk2_req_pcc5", - "hdmi_int_pn7", - - "ddc_scl_pv4", - "ddc_sda_pv5", - - "pu6", - - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - - "dap4_din_pp5", - "dap4_sclk_pp7", - - "clk3_out_pee0", - "clk3_req_pee1", - - "sdmmc4_dat5_paa5", - "gpio_pcc1", - "cam_i2c_scl_pbb1", - "cam_i2c_sda_pbb2", - "pbb5", - "pbb7", - "jtag_rtck", - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", - - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row2_pr2", - "kb_row4_pr4", - "kb_row5_pr5", - "kb_row9_ps1", - "kb_row10_ps2", - "kb_row11_ps3", - "kb_row12_ps4", - "kb_row15_ps7", - - "clk_32k_out_pa0", - "core_pwr_req", - "cpu_pwr_req", - "pwr_int_n", - "clk_32k_in", - "owr", - - "dap_mclk1_pw4", - "spdif_in_pk6", - "spdif_out_pk5", - "sdmmc3_clk_pa6", - "sdmmc3_dat0_pb7", - - "pex_l0_rst_n_pdd1", - "pex_l0_clkreq_n_pdd2", - "pex_wake_n_pdd3", - "pex_l1_rst_n_pdd5", - "pex_l1_clkreq_n_pdd6", - "hdmi_cec_pee3", - - "sdmmc3_cd_n_pv2", - "usb_vbus_en0_pn4", - "usb_vbus_en1_pn5", - "sdmmc3_clk_lb_out_pee4", - "sdmmc3_clk_lb_in_pee5", - "reset_out_n", - "kb_row16_pt0", - "kb_row17_pt1", - "dp_hpd_pff0", - "usb_vbus_en2_pff1", - "pff2", -}; - -static const char * const rsvd4_groups[] = { - "dap3_dout_pp2", - "pv0", - "pv1", - "sdmmc1_clk_pz0", - - "clk2_out_pw5", - "clk2_req_pcc5", - "hdmi_int_pn7", - "ddc_scl_pv4", - "ddc_sda_pv5", - - "uart2_rts_n_pj6", - "uart2_cts_n_pj5", - "uart3_txd_pw6", - "uart3_rxd_pw7", - - "pu0", - "pu1", - "pu2", - - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - - "dap4_fs_pp4", - "dap4_dout_pp6", - "dap4_din_pp5", - "dap4_sclk_pp7", - - "clk3_out_pee0", - "clk3_req_pee1", - - "pi5", - "pk1", - "pk2", - "pg0", - "pg1", - "pg2", - "pg3", - "ph4", - "ph5", - "pb0", - "pb1", - "pk7", - "pi0", - "pi1", - "pi2", - - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", - - "sdmmc4_cmd_pt7", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "sdmmc4_dat4_paa4", - "sdmmc4_dat5_paa5", - "sdmmc4_dat6_paa6", - "sdmmc4_dat7_paa7", - - "jtag_rtck", - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", - - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row2_pr2", - "kb_row13_ps5", - "kb_row14_ps6", - "kb_row15_ps7", - - "kb_col0_pq0", - "kb_col1_pq1", - "kb_col2_pq2", - "kb_col5_pq5", - - "clk_32k_out_pa0", - "core_pwr_req", - "cpu_pwr_req", - "pwr_int_n", - "clk_32k_in", - "owr", - - "dap1_fs_pn0", - "dap1_din_pn1", - "dap1_sclk_pn3", - "dap_mclk1_req_pee2", - "dap_mclk1_pw5", - - "dap2_fs_pa2", - "dap2_din_pa4", - "dap2_dout_pa5", - "dap2_sclk_pa3", - - "dvfs_pwm_px0", - "dvfs_clk_px2", - "gpio_x1_aud_px1", - "gpio_x3_aud_px3", - - "gpio_x5_aud_px5", - "gpio_x7_aud_px7", - - "pex_l0_rst_n_pdd1", - "pex_l0_clkreq_n_pdd2", - "pex_wake_n_pdd3", - "pex_l1_rst_n_pdd5", - "pex_l1_clkreq_n_pdd6", - "hdmi_cec_pee3", - - "sdmmc3_cd_n_pv2", - "usb_vbus_en0_pn4", - "usb_vbus_en1_pn5", - "sdmmc3_clk_lb_out_pee4", - "sdmmc3_clk_lb_in_pee5", - "gmi_clk_lb", - - "dp_hpd_pff0", - "usb_vbus_en2_pff1", - "pff2", -}; - -static const char * const sdmmc1_groups[] = { - "sdmmc1_clk_pz0", - "sdmmc1_cmd_pz1", - "sdmmc1_dat3_py4", - "sdmmc1_dat2_py5", - "sdmmc1_dat1_py6", - "sdmmc1_dat0_py7", - "clk2_out_pw5", - "clk2_req_pcc", - "uart3_cts_n_pa1", - "sdmmc1_wp_n_pv3", -}; - -static const char * const sdmmc2_groups[] = { - "pi5", - "pk1", - "pk3", - "pk4", - "pi6", - "ph4", - "ph5", - "ph6", - "ph7", - "pi2", - "cam_mclk_pcc0", - "pcc1", - "pbb0", - "cam_i2c_scl_pbb1", - "cam_i2c_sda_pbb2", - "pbb3", - "pbb4", - "pbb5", - "pbb6", - "pbb7", - "pcc2", - "gmi_clk_lb", -}; - -static const char * const sdmmc3_groups[] = { - "pk0", - "pcc2", - - "kb_col4_pq4", - "kb_col5_pq5", - - "sdmmc3_clk_pa6", - "sdmmc3_cmd_pa7", - "sdmmc3_dat0_pb7", - "sdmmc3_dat1_pb6", - "sdmmc3_dat2_pb5", - "sdmmc3_dat3_pb4", - - "sdmmc3_cd_n_pv2", - "sdmmc3_clk_lb_in_pee5", - "sdmmc3_clk_lb_out_pee4", -}; - -static const char * const sdmmc4_groups[] = { - "sdmmc4_clk_pcc4", - "sdmmc4_cmd_pt7", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "sdmmc4_dat4_paa4", - "sdmmc4_dat5_paa5", - "sdmmc4_dat6_paa6", - "sdmmc4_dat7_paa7", -}; - -static const char * const soc_groups[] = { - "pk0", - "pj2", - "kb_row15_ps7", - "clk_32k_out_pa0", -}; - -static const char * const spdif_groups[] = { - "sdmmc1_cmd_pz1", - "sdmmc1_dat3_py4", - "uart2_rxd_pc3", - "uart2_txd_pc2", - "spdif_in_pk6", - "spdif_out_pk5", -}; - -static const char * const spi1_groups[] = { - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", - "gpio_x3_aud_px3", - "gpio_x4_aud_px4", - "gpio_x5_aud_px5", - "gpio_x6_aud_px6", - "gpio_x7_aud_px7", - "gpio_w3_aud_pw3", -}; - -static const char * const spi2_groups[] = { - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", - - "kb_row13_ps5", - "kb_row14_ps6", - "kb_row15_ps7", - "kb_col0_pq0", - "kb_col1_pq1", - "kb_col2_pq2", - "kb_col6_pq6", - "kb_col7_pq7", - "gpio_x4_aud_px4", - "gpio_x5_aud_px5", - "gpio_x6_aud_px6", - "gpio_x7_aud_px7", - "gpio_w2_aud_pw2", - "gpio_w3_aud_pw3", -}; - -static const char * const spi3_groups[] = { - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "sdmmc4_dat4_paa4", - "sdmmc4_dat5_paa5", - "sdmmc4_dat6_paa6", - "sdmmc3_clk_pa6", - "sdmmc3_cmd_pa7", - "sdmmc3_dat0_pb7", - "sdmmc3_dat1_pb6", - "sdmmc3_dat2_pb5", - "sdmmc3_dat3_pb4", -}; - -static const char * const spi4_groups[] = { - "sdmmc1_cmd_pz1", - "sdmmc1_dat3_py4", - "sdmmc1_dat2_py5", - "sdmmc1_dat1_py6", - "sdmmc1_dat0_py7", - - "uart2_rxd_pc3", - "uart2_txd_pc2", - "uart2_rts_n_pj6", - "uart2_cts_n_pj5", - "uart3_txd_pw6", - "uart3_rxd_pw7", - - "pi3", - "pg4", - "pg5", - "pg6", - "pg7", - "ph3", - "pi4", - "sdmmc1_wp_n_pv3", -}; - -static const char * const spi5_groups[] = { - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", - "dap3_fs_pp0", - "dap3_din_pp1", - "dap3_dout_pp2", - "dap3_sclk_pp3", -}; - -static const char * const spi6_groups[] = { - "dvfs_pwm_px0", - "gpio_x1_aud_px1", - "gpio_x3_aud_px3", - "dvfs_clk_px2", - "gpio_x6_aud_px6", - "gpio_w2_aud_pw2", - "gpio_w3_aud_pw3", -}; - -static const char * const trace_groups[] = { - "pi2", - "pi4", - "pi7", - "ph0", - "ph6", - "ph7", - "pg2", - "pg3", - "pk1", - "pk3", -}; - -static const char * const uarta_groups[] = { - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", - - "sdmmc1_cmd_pz1", - "sdmmc1_dat3_py4", - "sdmmc1_dat2_py5", - "sdmmc1_dat1_py6", - "sdmmc1_dat0_py7", - - - "uart2_rxd_pc3", - "uart2_txd_pc2", - "uart2_rts_n_pj6", - "uart2_cts_n_pj5", - - "pu0", - "pu1", - "pu2", - "pu3", - "pu4", - "pu5", - "pu6", - - "kb_row7_pr7", - "kb_row8_ps0", - "kb_row9_ps1", - "kb_row10_ps2", - "kb_col3_pq3", - "kb_col4_pq4", - - "sdmmc3_cmd_pa7", - "sdmmc3_dat1_pb6", - "sdmmc1_wp_n_pv3", - -}; - -static const char * const uartb_groups[] = { - "uart2_rts_n_pj6", - "uart2_cts_n_pj5", -}; - -static const char * const uartc_groups[] = { - "uart3_txd_pw6", - "uart3_rxd_pw7", - "uart3_cts_n_pa1", - "uart3_rts_n_pc0", - "kb_row16_pt0", - "kn_row17_pt1", -}; - -static const char * const uartd_groups[] = { - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", - "pj7", - "pb0", - "pb1", - "pk7", - "kb_col6_pq6", - "kb_col7_pq7", -}; - -static const char * const ulpi_groups[] = { - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", -}; - -static const char * const usb_groups[] = { - "pj0", - "usb_vbus_en0_pn4", - "usb_vbus_en1_pn5", - "usb_vbus_en2_pff1", -}; - -static const char * const vgp1_groups[] = { - "cam_i2c_scl_pbb1", -}; - -static const char * const vgp2_groups[] = { - "cam_i2c_sda_pbb2", -}; - -static const char * const vgp3_groups[] = { - "pbb3", -}; - -static const char * const vgp4_groups[] = { - "pbb4", -}; - -static const char * const vgp5_groups[] = { - "pbb5", -}; - -static const char * const vgp6_groups[] = { - "pbb0", -}; - -static const char * const vi_groups[] = { - "cam_mclk_pcc0", -}; - -static const char * const vi_alt1_groups[] = { - "cam_mclk_pcc0", -}; - -static const char * const vi_alt3_groups[] = { - "cam_mclk_pcc0", -}; - -static const char * const vimclk2_groups[] = { - "pbb0", -}; - -static const char * const vimclk2_alt_groups[] = { - "pbb0", -}; - -static const char * const sata_groups[] = { - "dap_mclk1_req_pee2", - "dap1_dout_pn2", - "pff2", -}; - -static const char * const ccla_groups[] = { - "pk3", -}; - -static const char * const rtck_groups[] = { - "jtag_rtck", -}; - -static const char * const sys_groups[] = { - "kb_row3_pr3", -}; - -static const char * const pe0_groups[] = { - "pex_l0_rst_n_pdd1", - "pex_l0_clkreq_n_pdd2", -}; - -static const char * const pe_groups[] = { - "pex_wake_n_pdd3", -}; - -static const char * const pe1_groups[] = { - "pex_l1_rst_n_pdd5", - "pex_l1_clkreq_n_pdd6", -}; - -static const char * const dp_groups[] = { - "dp_hpd_pff0", -}; - -static const char * const clk_groups[] = { - "clk_32k_in", -}; - -static const char * const tmds_groups[] = { - "pg4", - "ph1", - "ph2", }; #define FUNCTION(fname) \ { \ .name = #fname, \ - .groups = fname##_groups, \ - .ngroups = ARRAY_SIZE(fname##_groups), \ } -static const struct tegra_function tegra124_functions[] = { +static struct tegra_function tegra124_functions[] = { FUNCTION(blink), + FUNCTION(ccla), FUNCTION(cec), FUNCTION(cldvfs), + FUNCTION(clk), FUNCTION(clk12), FUNCTION(cpu), FUNCTION(dap), @@ -2706,6 +1602,7 @@ static const struct tegra_function tegra124_functions[] = { FUNCTION(displaya), FUNCTION(displaya_alt), FUNCTION(displayb), + FUNCTION(dp), FUNCTION(dtv), FUNCTION(extperiph1), FUNCTION(extperiph2), @@ -2727,6 +1624,9 @@ static const struct tegra_function tegra124_functions[] = { FUNCTION(irda), FUNCTION(kbc), FUNCTION(owr), + FUNCTION(pe), + FUNCTION(pe0), + FUNCTION(pe1), FUNCTION(pmi), FUNCTION(pwm0), FUNCTION(pwm1), @@ -2738,6 +1638,8 @@ static const struct tegra_function tegra124_functions[] = { FUNCTION(rsvd2), FUNCTION(rsvd3), FUNCTION(rsvd4), + FUNCTION(rtck), + FUNCTION(sata), FUNCTION(sdmmc1), FUNCTION(sdmmc2), FUNCTION(sdmmc3), @@ -2750,6 +1652,8 @@ static const struct tegra_function tegra124_functions[] = { FUNCTION(spi4), FUNCTION(spi5), FUNCTION(spi6), + FUNCTION(sys), + FUNCTION(tmds), FUNCTION(trace), FUNCTION(uarta), FUNCTION(uartb), @@ -2768,23 +1672,13 @@ static const struct tegra_function tegra124_functions[] = { FUNCTION(vi_alt3), FUNCTION(vimclk2), FUNCTION(vimclk2_alt), - FUNCTION(sata), - FUNCTION(ccla), - FUNCTION(pe0), - FUNCTION(pe), - FUNCTION(pe1), - FUNCTION(dp), - FUNCTION(rtck), - FUNCTION(sys), - FUNCTION(clk), - FUNCTION(tmds), }; -#define DRV_PINGROUP_REG_A 0x868 /* bank 0 */ -#define PINGROUP_REG_A 0x3000 /* bank 1 */ +#define DRV_PINGROUP_REG_A 0x868 /* bank 0 */ +#define PINGROUP_REG_A 0x3000 /* bank 1 */ -#define PINGROUP_REG_Y(r) ((r) - PINGROUP_REG_A) -#define PINGROUP_REG_N(r) -1 +#define PINGROUP_REG_Y(r) ((r) - PINGROUP_REG_A) +#define PINGROUP_REG_N(r) -1 #define PINGROUP(pg_name, f0, f1, f2, f3, f_safe, r, od, ior, rcv_sel) \ { \ @@ -2792,12 +1686,12 @@ static const struct tegra_function tegra124_functions[] = { .pins = pg_name##_pins, \ .npins = ARRAY_SIZE(pg_name##_pins), \ .funcs = { \ - TEGRA_MUX_ ## f0, \ - TEGRA_MUX_ ## f1, \ - TEGRA_MUX_ ## f2, \ - TEGRA_MUX_ ## f3, \ + TEGRA_MUX_##f0, \ + TEGRA_MUX_##f1, \ + TEGRA_MUX_##f2, \ + TEGRA_MUX_##f3, \ }, \ - .func_safe = TEGRA_MUX_ ## f_safe, \ + .func_safe = TEGRA_MUX_##f_safe, \ .mux_reg = PINGROUP_REG_Y(r), \ .mux_bank = 1, \ .mux_bit = 0, \ @@ -2826,8 +1720,9 @@ static const struct tegra_function tegra124_functions[] = { .drvtype_reg = -1, \ } -#define DRV_PINGROUP_DVRTYPE_Y(r) ((r) - DRV_PINGROUP_REG_A) -#define DRV_PINGROUP_DVRTYPE_N(r) -1 +#define DRV_PINGROUP_REG_Y(r) ((r) - DRV_PINGROUP_REG_A) +#define DRV_PINGROUP_REG_N(r) -1 + #define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, \ drvdn_b, drvdn_w, drvup_b, drvup_w, \ @@ -2845,7 +1740,7 @@ static const struct tegra_function tegra124_functions[] = { .lock_reg = -1, \ .ioreset_reg = -1, \ .rcv_sel_reg = -1, \ - .drv_reg = DRV_PINGROUP_DVRTYPE_Y(r), \ + .drv_reg = DRV_PINGROUP_REG_Y(r), \ .drv_bank = 0, \ .hsm_bit = hsm_b, \ .schmitt_bit = schmitt_b, \ @@ -2858,7 +1753,7 @@ static const struct tegra_function tegra124_functions[] = { .slwr_width = slwr_w, \ .slwf_bit = slwf_b, \ .slwf_width = slwf_w, \ - .drvtype_reg = DRV_PINGROUP_DVRTYPE_##drvtype(r), \ + .drvtype_reg = DRV_PINGROUP_REG_##drvtype(r), \ .drvtype_bank = 0, \ .drvtype_bit = 6, \ } @@ -2909,8 +1804,8 @@ static const struct tegra_pingroup tegra124_groups[] = { PINGROUP(pu4, PWM1, UARTA, GMI, DISPLAYB, PWM1, 0x3194, N, N, N), PINGROUP(pu5, PWM2, UARTA, GMI, DISPLAYB, PWM2, 0x3198, N, N, N), PINGROUP(pu6, PWM3, UARTA, RSVD3, GMI, RSVD3, 0x319c, N, N, N), - PINGROUP(gen1_i2c_scl_pc4, I2C1, RSVD2, RSVD3, RSVD4, I2C1, 0x31a0, Y, N, N), - PINGROUP(gen1_i2c_sda_pc5, I2C1, RSVD2, RSVD3, RSVD4, I2C1, 0x31a4, Y, N, N), + PINGROUP(gen1_i2c_sda_pc5, I2C1, RSVD2, RSVD3, RSVD4, I2C1, 0x31a0, Y, N, N), + PINGROUP(gen1_i2c_scl_pc4, I2C1, RSVD2, RSVD3, RSVD4, I2C1, 0x31a4, Y, N, N), PINGROUP(dap4_fs_pp4, I2S3, GMI, DTV, RSVD4, I2S3, 0x31a8, N, N, N), PINGROUP(dap4_din_pp5, I2S3, GMI, RSVD3, RSVD4, I2S3, 0x31ac, N, N, N), PINGROUP(dap4_dout_pp6, I2S3, GMI, DTV, RSVD4, I2S3, 0x31b0, N, N, N), @@ -2964,9 +1859,9 @@ static const struct tegra_pingroup tegra124_groups[] = { PINGROUP(sdmmc4_dat4_paa4, SDMMC4, SPI3, GMI, RSVD4, SDMMC4, 0x3270, N, Y, N), PINGROUP(sdmmc4_dat5_paa5, SDMMC4, SPI3, RSVD3, RSVD4, SDMMC4, 0x3274, N, Y, N), PINGROUP(sdmmc4_dat6_paa6, SDMMC4, SPI3, GMI, RSVD4, SDMMC4, 0x3278, N, Y, N), - PINGROUP(sdmmc4_dat7_paa7, SDMMC4, RSVD1, GMI, RSVD4, SDMMC4, 0x327c, N, Y, N), + PINGROUP(sdmmc4_dat7_paa7, SDMMC4, RSVD2, GMI, RSVD4, SDMMC4, 0x327c, N, Y, N), PINGROUP(cam_mclk_pcc0, VI, VI_ALT1, VI_ALT3, SDMMC2, VI, 0x3284, N, N, N), - PINGROUP(pcc1, I2S4, RSVD1, RSVD3, SDMMC2, I2S4, 0x3288, N, N, N), + PINGROUP(pcc1, I2S4, RSVD2, RSVD3, SDMMC2, I2S4, 0x3288, N, N, N), PINGROUP(pbb0, VGP6, VIMCLK2, SDMMC2, VIMCLK2_ALT, VGP6, 0x328c, N, N, N), PINGROUP(cam_i2c_scl_pbb1, VGP1, I2C3, RSVD3, SDMMC2, VGP1, 0x3290, Y, N, N), PINGROUP(cam_i2c_sda_pbb2, VGP2, I2C3, RSVD3, SDMMC2, VGP2, 0x3294, Y, N, N), @@ -3047,8 +1942,8 @@ static const struct tegra_pingroup tegra124_groups[] = { PINGROUP(gpio_w3_aud_pw3, SPI6, SPI1, SPI2, I2C1, SPI1, 0x33f0, N, N, N), PINGROUP(usb_vbus_en0_pn4, USB, RSVD2, RSVD3, RSVD4, USB, 0x33f4, Y, N, N), PINGROUP(usb_vbus_en1_pn5, USB, RSVD2, RSVD3, RSVD4, USB, 0x33f8, Y, N, N), - PINGROUP(sdmmc3_clk_lb_out_pee4, SDMMC3, RSVD2, RSVD3, RSVD4, SDMMC3, 0x33fc, N, N, N), - PINGROUP(sdmmc3_clk_lb_in_pee5, SDMMC3, RSVD2, RSVD3, RSVD4, SDMMC3, 0x3400, N, N, N), + PINGROUP(sdmmc3_clk_lb_in_pee5, SDMMC3, RSVD2, RSVD3, RSVD4, SDMMC3, 0x33fc, N, N, N), + PINGROUP(sdmmc3_clk_lb_out_pee4, SDMMC3, RSVD2, RSVD3, RSVD4, SDMMC3, 0x3400, N, N, N), PINGROUP(gmi_clk_lb, SDMMC2, RSVD2, GMI, RSVD4, SDMMC2, 0x3404, N, N, N), PINGROUP(reset_out_n, RSVD1, RSVD2, RSVD3, RESET_OUT_N, RSVD1, 0x3408, N, N, N), PINGROUP(kb_row16_pt0, KBC, RSVD2, RSVD3, UARTC, KBC, 0x340c, N, N, N), diff --git a/drivers/pinctrl/pinctrl-tegra20.c b/drivers/pinctrl/pinctrl-tegra20.c index fcfb7d012c5b..e0b504088387 100644 --- a/drivers/pinctrl/pinctrl-tegra20.c +++ b/drivers/pinctrl/pinctrl-tegra20.c @@ -1894,637 +1894,12 @@ enum tegra_mux { TEGRA_MUX_XIO, }; -static const char * const ahb_clk_groups[] = { - "cdev2", -}; - -static const char * const apb_clk_groups[] = { - "cdev2", -}; - -static const char * const audio_sync_groups[] = { - "cdev1", -}; - -static const char * const crt_groups[] = { - "crtp", - "lm1", -}; - -static const char * const dap1_groups[] = { - "dap1", -}; - -static const char * const dap2_groups[] = { - "dap2", -}; - -static const char * const dap3_groups[] = { - "dap3", -}; - -static const char * const dap4_groups[] = { - "dap4", -}; - -static const char * const dap5_groups[] = { - "gme", -}; - -static const char * const displaya_groups[] = { - "lcsn", - "ld0", - "ld1", - "ld10", - "ld11", - "ld12", - "ld13", - "ld14", - "ld15", - "ld16", - "ld17", - "ld2", - "ld3", - "ld4", - "ld5", - "ld6", - "ld7", - "ld8", - "ld9", - "ldc", - "ldi", - "lhp0", - "lhp1", - "lhp2", - "lhs", - "lm0", - "lm1", - "lpp", - "lpw0", - "lpw1", - "lpw2", - "lsc0", - "lsc1", - "lsck", - "lsda", - "lsdi", - "lspi", - "lvp0", - "lvp1", - "lvs", -}; - -static const char * const displayb_groups[] = { - "lcsn", - "ld0", - "ld1", - "ld10", - "ld11", - "ld12", - "ld13", - "ld14", - "ld15", - "ld16", - "ld17", - "ld2", - "ld3", - "ld4", - "ld5", - "ld6", - "ld7", - "ld8", - "ld9", - "ldc", - "ldi", - "lhp0", - "lhp1", - "lhp2", - "lhs", - "lm0", - "lm1", - "lpp", - "lpw0", - "lpw1", - "lpw2", - "lsc0", - "lsc1", - "lsck", - "lsda", - "lsdi", - "lspi", - "lvp0", - "lvp1", - "lvs", -}; - -static const char * const emc_test0_dll_groups[] = { - "kbca", -}; - -static const char * const emc_test1_dll_groups[] = { - "kbcc", -}; - -static const char * const gmi_groups[] = { - "ata", - "atb", - "atc", - "atd", - "ate", - "dap1", - "dap2", - "dap4", - "gma", - "gmb", - "gmc", - "gmd", - "gme", - "gpu", - "irrx", - "irtx", - "pta", - "spia", - "spib", - "spic", - "spid", - "spie", - "uca", - "ucb", -}; - -static const char * const gmi_int_groups[] = { - "gmb", -}; - -static const char * const hdmi_groups[] = { - "hdint", - "lpw0", - "lpw2", - "lsc1", - "lsck", - "lsda", - "lspi", - "pta", -}; - -static const char * const i2cp_groups[] = { - "i2cp", -}; - -static const char * const i2c1_groups[] = { - "rm", - "spdi", - "spdo", - "spig", - "spih", -}; - -static const char * const i2c2_groups[] = { - "ddc", - "pta", -}; - -static const char * const i2c3_groups[] = { - "dtf", -}; - -static const char * const ide_groups[] = { - "ata", - "atb", - "atc", - "atd", - "ate", - "gmb", -}; - -static const char * const irda_groups[] = { - "uad", -}; - -static const char * const kbc_groups[] = { - "kbca", - "kbcb", - "kbcc", - "kbcd", - "kbce", - "kbcf", -}; - -static const char * const mio_groups[] = { - "kbcb", - "kbcd", - "kbcf", -}; - -static const char * const mipi_hs_groups[] = { - "uaa", - "uab", -}; - -static const char * const nand_groups[] = { - "ata", - "atb", - "atc", - "atd", - "ate", - "gmb", - "gmd", - "kbca", - "kbcb", - "kbcc", - "kbcd", - "kbce", - "kbcf", -}; - -static const char * const osc_groups[] = { - "cdev1", - "cdev2", -}; - -static const char * const owr_groups[] = { - "kbce", - "owc", - "uac", -}; - -static const char * const pcie_groups[] = { - "gpv", - "slxa", - "slxk", -}; - -static const char * const plla_out_groups[] = { - "cdev1", -}; - -static const char * const pllc_out1_groups[] = { - "csus", -}; - -static const char * const pllm_out1_groups[] = { - "cdev1", -}; - -static const char * const pllp_out2_groups[] = { - "csus", -}; - -static const char * const pllp_out3_groups[] = { - "csus", -}; - -static const char * const pllp_out4_groups[] = { - "cdev2", -}; - -static const char * const pwm_groups[] = { - "gpu", - "sdb", - "sdc", - "sdd", - "ucb", -}; - -static const char * const pwr_intr_groups[] = { - "pmc", -}; - -static const char * const pwr_on_groups[] = { - "pmc", -}; - -static const char * const rsvd1_groups[] = { - "dta", - "dtb", - "dtc", - "dtd", - "dte", - "gmd", - "gme", -}; - -static const char * const rsvd2_groups[] = { - "crtp", - "dap1", - "dap3", - "dap4", - "ddc", - "dtb", - "dtc", - "dte", - "dtf", - "gpu7", - "gpv", - "hdint", - "i2cp", - "owc", - "rm", - "sdio1", - "spdi", - "spdo", - "uac", - "uca", - "uda", -}; - -static const char * const rsvd3_groups[] = { - "crtp", - "dap2", - "dap3", - "ddc", - "gpu7", - "gpv", - "hdint", - "i2cp", - "ld17", - "ldc", - "ldi", - "lhp0", - "lhp1", - "lhp2", - "lm1", - "lpp", - "lpw1", - "lvp0", - "lvp1", - "owc", - "pmc", - "rm", - "uac", -}; - -static const char * const rsvd4_groups[] = { - "ata", - "ate", - "crtp", - "dap3", - "dap4", - "ddc", - "dta", - "dtc", - "dtd", - "dtf", - "gpu", - "gpu7", - "gpv", - "hdint", - "i2cp", - "kbce", - "lcsn", - "ld0", - "ld1", - "ld2", - "ld3", - "ld4", - "ld5", - "ld6", - "ld7", - "ld8", - "ld9", - "ld10", - "ld11", - "ld12", - "ld13", - "ld14", - "ld15", - "ld16", - "ld17", - "ldc", - "ldi", - "lhp0", - "lhp1", - "lhp2", - "lhs", - "lm0", - "lpp", - "lpw1", - "lsc0", - "lsdi", - "lvp0", - "lvp1", - "lvs", - "owc", - "pmc", - "pta", - "rm", - "spif", - "uac", - "uca", - "ucb", -}; - -static const char * const rtck_groups[] = { - "gpu7", -}; - -static const char * const sdio1_groups[] = { - "sdio1", -}; - -static const char * const sdio2_groups[] = { - "dap1", - "dta", - "dtd", - "kbca", - "kbcb", - "kbcd", - "spdi", - "spdo", -}; - -static const char * const sdio3_groups[] = { - "sdb", - "sdc", - "sdd", - "slxa", - "slxc", - "slxd", - "slxk", -}; - -static const char * const sdio4_groups[] = { - "atb", - "atc", - "atd", - "gma", - "gme", -}; - -static const char * const sflash_groups[] = { - "gmc", - "gmd", -}; - -static const char * const spdif_groups[] = { - "slxc", - "slxd", - "spdi", - "spdo", - "uad", -}; - -static const char * const spi1_groups[] = { - "dtb", - "dte", - "spia", - "spib", - "spic", - "spid", - "spie", - "spif", - "uda", -}; - -static const char * const spi2_groups[] = { - "sdb", - "slxa", - "slxc", - "slxd", - "slxk", - "spia", - "spib", - "spic", - "spid", - "spie", - "spif", - "spig", - "spih", - "uab", -}; - -static const char * const spi2_alt_groups[] = { - "spid", - "spie", - "spig", - "spih", -}; - -static const char * const spi3_groups[] = { - "gma", - "lcsn", - "lm0", - "lpw0", - "lpw2", - "lsc1", - "lsck", - "lsda", - "lsdi", - "sdc", - "sdd", - "spia", - "spib", - "spic", - "spif", - "spig", - "spih", - "uaa", -}; - -static const char * const spi4_groups[] = { - "gmc", - "irrx", - "irtx", - "slxa", - "slxc", - "slxd", - "slxk", - "uad", -}; - -static const char * const trace_groups[] = { - "kbcc", - "kbcf", -}; - -static const char * const twc_groups[] = { - "dap2", - "sdc", -}; - -static const char * const uarta_groups[] = { - "gpu", - "irrx", - "irtx", - "sdb", - "sdd", - "sdio1", - "uaa", - "uab", - "uad", -}; - -static const char * const uartb_groups[] = { - "irrx", - "irtx", -}; - -static const char * const uartc_groups[] = { - "uca", - "ucb", -}; - -static const char * const uartd_groups[] = { - "gmc", - "uda", -}; - -static const char * const uarte_groups[] = { - "gma", - "sdio1", -}; - -static const char * const ulpi_groups[] = { - "uaa", - "uab", - "uda", -}; - -static const char * const vi_groups[] = { - "dta", - "dtb", - "dtc", - "dtd", - "dte", - "dtf", -}; - -static const char * const vi_sensor_clk_groups[] = { - "csus", -}; - -static const char * const xio_groups[] = { - "ld0", - "ld1", - "ld10", - "ld11", - "ld12", - "ld13", - "ld14", - "ld15", - "ld16", - "ld2", - "ld3", - "ld4", - "ld5", - "ld6", - "ld7", - "ld8", - "ld9", - "lhs", - "lsc0", - "lspi", - "lvs", -}; - #define FUNCTION(fname) \ { \ .name = #fname, \ - .groups = fname##_groups, \ - .ngroups = ARRAY_SIZE(fname##_groups), \ } -static const struct tegra_function tegra20_functions[] = { +static struct tegra_function tegra20_functions[] = { FUNCTION(ahb_clk), FUNCTION(apb_clk), FUNCTION(audio_sync), @@ -2881,18 +2256,7 @@ static struct platform_driver tegra20_pinctrl_driver = { .probe = tegra20_pinctrl_probe, .remove = tegra_pinctrl_remove, }; - -static int __init tegra20_pinctrl_init(void) -{ - return platform_driver_register(&tegra20_pinctrl_driver); -} -arch_initcall(tegra20_pinctrl_init); - -static void __exit tegra20_pinctrl_exit(void) -{ - platform_driver_unregister(&tegra20_pinctrl_driver); -} -module_exit(tegra20_pinctrl_exit); +module_platform_driver(tegra20_pinctrl_driver); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("NVIDIA Tegra20 pinctrl driver"); diff --git a/drivers/pinctrl/pinctrl-tegra30.c b/drivers/pinctrl/pinctrl-tegra30.c index 2300deba25bd..41d24f5c2854 100644 --- a/drivers/pinctrl/pinctrl-tegra30.c +++ b/drivers/pinctrl/pinctrl-tegra30.c @@ -25,7 +25,7 @@ * Most pins affected by the pinmux can also be GPIOs. Define these first. * These must match how the GPIO driver names/numbers its pins. */ -#define _GPIO(offset) (offset) +#define _GPIO(offset) (offset) #define TEGRA_PIN_CLK_32K_OUT_PA0 _GPIO(0) #define TEGRA_PIN_UART3_CTS_N_PA1 _GPIO(1) @@ -277,8 +277,8 @@ #define TEGRA_PIN_PEE7 _GPIO(247) /* All non-GPIO pins follow */ -#define NUM_GPIOS (TEGRA_PIN_PEE7 + 1) -#define _PIN(offset) (NUM_GPIOS + (offset)) +#define NUM_GPIOS (TEGRA_PIN_PEE7 + 1) +#define _PIN(offset) (NUM_GPIOS + (offset)) /* Non-GPIO pins */ #define TEGRA_PIN_CLK_32K_IN _PIN(0) @@ -2015,1253 +2015,13 @@ enum tegra_mux { TEGRA_MUX_VI_ALT2, TEGRA_MUX_VI_ALT3, }; -static const char * const blink_groups[] = { - "clk_32k_out_pa0", -}; - -static const char * const cec_groups[] = { - "hdmi_cec_pee3", - "owr", -}; - -static const char * const clk_12m_out_groups[] = { - "pv3", -}; - -static const char * const clk_32k_in_groups[] = { - "clk_32k_in", -}; - -static const char * const core_pwr_req_groups[] = { - "core_pwr_req", -}; - -static const char * const cpu_pwr_req_groups[] = { - "cpu_pwr_req", -}; - -static const char * const crt_groups[] = { - "crt_hsync_pv6", - "crt_vsync_pv7", -}; - -static const char * const dap_groups[] = { - "clk1_req_pee2", - "clk2_req_pcc5", -}; - -static const char * const ddr_groups[] = { - "vi_d0_pt4", - "vi_d1_pd5", - "vi_d10_pt2", - "vi_d11_pt3", - "vi_d2_pl0", - "vi_d3_pl1", - "vi_d4_pl2", - "vi_d5_pl3", - "vi_d6_pl4", - "vi_d7_pl5", - "vi_d8_pl6", - "vi_d9_pl7", - "vi_hsync_pd7", - "vi_vsync_pd6", -}; - -static const char * const dev3_groups[] = { - "clk3_req_pee1", -}; - -static const char * const displaya_groups[] = { - "dap3_din_pp1", - "dap3_dout_pp2", - "dap3_fs_pp0", - "dap3_sclk_pp3", - "pbb3", - "pbb4", - "pbb5", - "pbb6", - "lcd_cs0_n_pn4", - "lcd_cs1_n_pw0", - "lcd_d0_pe0", - "lcd_d1_pe1", - "lcd_d10_pf2", - "lcd_d11_pf3", - "lcd_d12_pf4", - "lcd_d13_pf5", - "lcd_d14_pf6", - "lcd_d15_pf7", - "lcd_d16_pm0", - "lcd_d17_pm1", - "lcd_d18_pm2", - "lcd_d19_pm3", - "lcd_d2_pe2", - "lcd_d20_pm4", - "lcd_d21_pm5", - "lcd_d22_pm6", - "lcd_d23_pm7", - "lcd_d3_pe3", - "lcd_d4_pe4", - "lcd_d5_pe5", - "lcd_d6_pe6", - "lcd_d7_pe7", - "lcd_d8_pf0", - "lcd_d9_pf1", - "lcd_dc0_pn6", - "lcd_dc1_pd2", - "lcd_de_pj1", - "lcd_hsync_pj3", - "lcd_m1_pw1", - "lcd_pclk_pb3", - "lcd_pwr0_pb2", - "lcd_pwr1_pc1", - "lcd_pwr2_pc6", - "lcd_sck_pz4", - "lcd_sdin_pz2", - "lcd_sdout_pn5", - "lcd_vsync_pj4", - "lcd_wr_n_pz3", -}; - -static const char * const displayb_groups[] = { - "dap3_din_pp1", - "dap3_dout_pp2", - "dap3_fs_pp0", - "dap3_sclk_pp3", - "pbb3", - "pbb4", - "pbb5", - "pbb6", - "lcd_cs0_n_pn4", - "lcd_cs1_n_pw0", - "lcd_d0_pe0", - "lcd_d1_pe1", - "lcd_d10_pf2", - "lcd_d11_pf3", - "lcd_d12_pf4", - "lcd_d13_pf5", - "lcd_d14_pf6", - "lcd_d15_pf7", - "lcd_d16_pm0", - "lcd_d17_pm1", - "lcd_d18_pm2", - "lcd_d19_pm3", - "lcd_d2_pe2", - "lcd_d20_pm4", - "lcd_d21_pm5", - "lcd_d22_pm6", - "lcd_d23_pm7", - "lcd_d3_pe3", - "lcd_d4_pe4", - "lcd_d5_pe5", - "lcd_d6_pe6", - "lcd_d7_pe7", - "lcd_d8_pf0", - "lcd_d9_pf1", - "lcd_dc0_pn6", - "lcd_dc1_pd2", - "lcd_de_pj1", - "lcd_hsync_pj3", - "lcd_m1_pw1", - "lcd_pclk_pb3", - "lcd_pwr0_pb2", - "lcd_pwr1_pc1", - "lcd_pwr2_pc6", - "lcd_sck_pz4", - "lcd_sdin_pz2", - "lcd_sdout_pn5", - "lcd_vsync_pj4", - "lcd_wr_n_pz3", -}; - -static const char * const dtv_groups[] = { - "gmi_a17_pb0", - "gmi_a18_pb1", - "gmi_cs0_n_pj0", - "gmi_cs1_n_pj2", -}; - -static const char * const extperiph1_groups[] = { - "clk1_out_pw4", -}; - -static const char * const extperiph2_groups[] = { - "clk2_out_pw5", -}; - -static const char * const extperiph3_groups[] = { - "clk3_out_pee0", -}; - -static const char * const gmi_groups[] = { - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_fs_pn0", - "dap1_sclk_pn3", - "dap2_din_pa4", - "dap2_dout_pa5", - "dap2_fs_pa2", - "dap2_sclk_pa3", - "dap4_din_pp5", - "dap4_dout_pp6", - "dap4_fs_pp4", - "dap4_sclk_pp7", - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", - "gmi_a16_pj7", - "gmi_a17_pb0", - "gmi_a18_pb1", - "gmi_a19_pk7", - "gmi_ad0_pg0", - "gmi_ad1_pg1", - "gmi_ad10_ph2", - "gmi_ad11_ph3", - "gmi_ad12_ph4", - "gmi_ad13_ph5", - "gmi_ad14_ph6", - "gmi_ad15_ph7", - "gmi_ad2_pg2", - "gmi_ad3_pg3", - "gmi_ad4_pg4", - "gmi_ad5_pg5", - "gmi_ad6_pg6", - "gmi_ad7_pg7", - "gmi_ad8_ph0", - "gmi_ad9_ph1", - "gmi_adv_n_pk0", - "gmi_clk_pk1", - "gmi_cs0_n_pj0", - "gmi_cs1_n_pj2", - "gmi_cs2_n_pk3", - "gmi_cs3_n_pk4", - "gmi_cs4_n_pk2", - "gmi_cs6_n_pi3", - "gmi_cs7_n_pi6", - "gmi_dqs_pi2", - "gmi_iordy_pi5", - "gmi_oe_n_pi1", - "gmi_rst_n_pi4", - "gmi_wait_pi7", - "gmi_wp_n_pc7", - "gmi_wr_n_pi0", - "pu0", - "pu1", - "pu2", - "pu3", - "pu4", - "pu5", - "pu6", - "sdmmc4_clk_pcc4", - "sdmmc4_cmd_pt7", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "sdmmc4_dat4_paa4", - "sdmmc4_dat5_paa5", - "sdmmc4_dat6_paa6", - "sdmmc4_dat7_paa7", - "spi1_cs0_n_px6", - "spi1_mosi_px4", - "spi1_sck_px5", - "spi2_cs0_n_px3", - "spi2_miso_px1", - "spi2_mosi_px0", - "spi2_sck_px2", - "uart2_cts_n_pj5", - "uart2_rts_n_pj6", - "uart3_cts_n_pa1", - "uart3_rts_n_pc0", - "uart3_rxd_pw7", - "uart3_txd_pw6", -}; - -static const char * const gmi_alt_groups[] = { - "gmi_a16_pj7", - "gmi_cs3_n_pk4", - "gmi_cs7_n_pi6", - "gmi_wp_n_pc7", -}; - -static const char * const hda_groups[] = { - "clk1_req_pee2", - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_fs_pn0", - "dap1_sclk_pn3", - "dap2_din_pa4", - "dap2_dout_pa5", - "dap2_fs_pa2", - "dap2_sclk_pa3", - "pex_l0_clkreq_n_pdd2", - "pex_l0_prsnt_n_pdd0", - "pex_l0_rst_n_pdd1", - "pex_l1_clkreq_n_pdd6", - "pex_l1_prsnt_n_pdd4", - "pex_l1_rst_n_pdd5", - "pex_l2_clkreq_n_pcc7", - "pex_l2_prsnt_n_pdd7", - "pex_l2_rst_n_pcc6", - "pex_wake_n_pdd3", - "spdif_in_pk6", -}; - -static const char * const hdcp_groups[] = { - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", - "lcd_pwr0_pb2", - "lcd_pwr2_pc6", - "lcd_sck_pz4", - "lcd_sdout_pn5", - "lcd_wr_n_pz3", -}; - -static const char * const hdmi_groups[] = { - "hdmi_int_pn7", -}; - -static const char * const hsi_groups[] = { - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", -}; - -static const char * const i2c1_groups[] = { - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - "spdif_in_pk6", - "spdif_out_pk5", - "spi2_cs1_n_pw2", - "spi2_cs2_n_pw3", -}; - -static const char * const i2c2_groups[] = { - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", -}; - -static const char * const i2c3_groups[] = { - "cam_i2c_scl_pbb1", - "cam_i2c_sda_pbb2", - "sdmmc4_cmd_pt7", - "sdmmc4_dat4_paa4", -}; - -static const char * const i2c4_groups[] = { - "ddc_scl_pv4", - "ddc_sda_pv5", -}; - -static const char * const i2cpwr_groups[] = { - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", -}; - -static const char * const i2s0_groups[] = { - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_fs_pn0", - "dap1_sclk_pn3", -}; - -static const char * const i2s1_groups[] = { - "dap2_din_pa4", - "dap2_dout_pa5", - "dap2_fs_pa2", - "dap2_sclk_pa3", -}; - -static const char * const i2s2_groups[] = { - "dap3_din_pp1", - "dap3_dout_pp2", - "dap3_fs_pp0", - "dap3_sclk_pp3", -}; - -static const char * const i2s3_groups[] = { - "dap4_din_pp5", - "dap4_dout_pp6", - "dap4_fs_pp4", - "dap4_sclk_pp7", -}; - -static const char * const i2s4_groups[] = { - "pbb0", - "pbb7", - "pcc1", - "pcc2", - "sdmmc4_dat4_paa4", - "sdmmc4_dat5_paa5", - "sdmmc4_dat6_paa6", - "sdmmc4_dat7_paa7", -}; - -static const char * const invalid_groups[] = { - "kb_row3_pr3", - "sdmmc4_clk_pcc4", -}; - -static const char * const kbc_groups[] = { - "kb_col0_pq0", - "kb_col1_pq1", - "kb_col2_pq2", - "kb_col3_pq3", - "kb_col4_pq4", - "kb_col5_pq5", - "kb_col6_pq6", - "kb_col7_pq7", - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row10_ps2", - "kb_row11_ps3", - "kb_row12_ps4", - "kb_row13_ps5", - "kb_row14_ps6", - "kb_row15_ps7", - "kb_row2_pr2", - "kb_row3_pr3", - "kb_row4_pr4", - "kb_row5_pr5", - "kb_row6_pr6", - "kb_row7_pr7", - "kb_row8_ps0", - "kb_row9_ps1", -}; - -static const char * const mio_groups[] = { - "kb_col6_pq6", - "kb_col7_pq7", - "kb_row10_ps2", - "kb_row11_ps3", - "kb_row12_ps4", - "kb_row13_ps5", - "kb_row14_ps6", - "kb_row15_ps7", - "kb_row6_pr6", - "kb_row7_pr7", - "kb_row8_ps0", - "kb_row9_ps1", -}; - -static const char * const nand_groups[] = { - "gmi_ad0_pg0", - "gmi_ad1_pg1", - "gmi_ad10_ph2", - "gmi_ad11_ph3", - "gmi_ad12_ph4", - "gmi_ad13_ph5", - "gmi_ad14_ph6", - "gmi_ad15_ph7", - "gmi_ad2_pg2", - "gmi_ad3_pg3", - "gmi_ad4_pg4", - "gmi_ad5_pg5", - "gmi_ad6_pg6", - "gmi_ad7_pg7", - "gmi_ad8_ph0", - "gmi_ad9_ph1", - "gmi_adv_n_pk0", - "gmi_clk_pk1", - "gmi_cs0_n_pj0", - "gmi_cs1_n_pj2", - "gmi_cs2_n_pk3", - "gmi_cs3_n_pk4", - "gmi_cs4_n_pk2", - "gmi_cs6_n_pi3", - "gmi_cs7_n_pi6", - "gmi_dqs_pi2", - "gmi_iordy_pi5", - "gmi_oe_n_pi1", - "gmi_rst_n_pi4", - "gmi_wait_pi7", - "gmi_wp_n_pc7", - "gmi_wr_n_pi0", - "kb_col0_pq0", - "kb_col1_pq1", - "kb_col2_pq2", - "kb_col3_pq3", - "kb_col4_pq4", - "kb_col5_pq5", - "kb_col6_pq6", - "kb_col7_pq7", - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row10_ps2", - "kb_row11_ps3", - "kb_row12_ps4", - "kb_row13_ps5", - "kb_row14_ps6", - "kb_row15_ps7", - "kb_row2_pr2", - "kb_row3_pr3", - "kb_row4_pr4", - "kb_row5_pr5", - "kb_row6_pr6", - "kb_row7_pr7", - "kb_row8_ps0", - "kb_row9_ps1", - "sdmmc4_clk_pcc4", - "sdmmc4_cmd_pt7", -}; - -static const char * const nand_alt_groups[] = { - "gmi_cs6_n_pi3", - "gmi_cs7_n_pi6", - "gmi_rst_n_pi4", -}; - -static const char * const owr_groups[] = { - "pu0", - "pv2", - "kb_row5_pr5", - "owr", -}; - -static const char * const pcie_groups[] = { - "pex_l0_clkreq_n_pdd2", - "pex_l0_prsnt_n_pdd0", - "pex_l0_rst_n_pdd1", - "pex_l1_clkreq_n_pdd6", - "pex_l1_prsnt_n_pdd4", - "pex_l1_rst_n_pdd5", - "pex_l2_clkreq_n_pcc7", - "pex_l2_prsnt_n_pdd7", - "pex_l2_rst_n_pcc6", - "pex_wake_n_pdd3", -}; - -static const char * const pwm0_groups[] = { - "gmi_ad8_ph0", - "pu3", - "sdmmc3_dat3_pb4", - "sdmmc3_dat5_pd0", - "uart3_rts_n_pc0", -}; - -static const char * const pwm1_groups[] = { - "gmi_ad9_ph1", - "pu4", - "sdmmc3_dat2_pb5", - "sdmmc3_dat4_pd1", -}; - -static const char * const pwm2_groups[] = { - "gmi_ad10_ph2", - "pu5", - "sdmmc3_clk_pa6", -}; - -static const char * const pwm3_groups[] = { - "gmi_ad11_ph3", - "pu6", - "sdmmc3_cmd_pa7", -}; - -static const char * const pwr_int_n_groups[] = { - "pwr_int_n", -}; - -static const char * const rsvd1_groups[] = { - "gmi_ad0_pg0", - "gmi_ad1_pg1", - "gmi_ad12_ph4", - "gmi_ad13_ph5", - "gmi_ad14_ph6", - "gmi_ad15_ph7", - "gmi_ad2_pg2", - "gmi_ad3_pg3", - "gmi_ad4_pg4", - "gmi_ad5_pg5", - "gmi_ad6_pg6", - "gmi_ad7_pg7", - "gmi_adv_n_pk0", - "gmi_clk_pk1", - "gmi_cs0_n_pj0", - "gmi_cs1_n_pj2", - "gmi_cs2_n_pk3", - "gmi_cs3_n_pk4", - "gmi_cs4_n_pk2", - "gmi_dqs_pi2", - "gmi_iordy_pi5", - "gmi_oe_n_pi1", - "gmi_wait_pi7", - "gmi_wp_n_pc7", - "gmi_wr_n_pi0", - "pu1", - "pu2", - "pv0", - "pv1", - "sdmmc3_dat0_pb7", - "sdmmc3_dat1_pb6", - "sdmmc3_dat2_pb5", - "sdmmc3_dat3_pb4", - "vi_pclk_pt0", -}; - -static const char * const rsvd2_groups[] = { - "clk1_out_pw4", - "clk2_out_pw5", - "clk2_req_pcc5", - "clk3_out_pee0", - "clk3_req_pee1", - "clk_32k_in", - "clk_32k_out_pa0", - "core_pwr_req", - "cpu_pwr_req", - "crt_hsync_pv6", - "crt_vsync_pv7", - "dap3_din_pp1", - "dap3_dout_pp2", - "dap3_fs_pp0", - "dap3_sclk_pp3", - "dap4_din_pp5", - "dap4_dout_pp6", - "dap4_fs_pp4", - "dap4_sclk_pp7", - "ddc_scl_pv4", - "ddc_sda_pv5", - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - "pbb0", - "pbb7", - "pcc1", - "pcc2", - "pv0", - "pv1", - "pv2", - "pv3", - "hdmi_cec_pee3", - "hdmi_int_pn7", - "jtag_rtck_pu7", - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", - "pwr_int_n", - "sdmmc1_clk_pz0", - "sdmmc1_cmd_pz1", - "sdmmc1_dat0_py7", - "sdmmc1_dat1_py6", - "sdmmc1_dat2_py5", - "sdmmc1_dat3_py4", - "sdmmc3_dat0_pb7", - "sdmmc3_dat1_pb6", - "sdmmc4_rst_n_pcc3", - "spdif_out_pk5", - "sys_clk_req_pz5", - "uart3_cts_n_pa1", - "uart3_rxd_pw7", - "uart3_txd_pw6", - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", - "vi_d0_pt4", - "vi_d10_pt2", - "vi_d11_pt3", - "vi_hsync_pd7", - "vi_vsync_pd6", -}; - -static const char * const rsvd3_groups[] = { - "cam_i2c_scl_pbb1", - "cam_i2c_sda_pbb2", - "clk1_out_pw4", - "clk1_req_pee2", - "clk2_out_pw5", - "clk2_req_pcc5", - "clk3_out_pee0", - "clk3_req_pee1", - "clk_32k_in", - "clk_32k_out_pa0", - "core_pwr_req", - "cpu_pwr_req", - "crt_hsync_pv6", - "crt_vsync_pv7", - "dap2_din_pa4", - "dap2_dout_pa5", - "dap2_fs_pa2", - "dap2_sclk_pa3", - "ddc_scl_pv4", - "ddc_sda_pv5", - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - "pbb0", - "pbb7", - "pcc1", - "pcc2", - "pv0", - "pv1", - "pv2", - "pv3", - "hdmi_cec_pee3", - "hdmi_int_pn7", - "jtag_rtck_pu7", - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row2_pr2", - "kb_row3_pr3", - "lcd_d0_pe0", - "lcd_d1_pe1", - "lcd_d10_pf2", - "lcd_d11_pf3", - "lcd_d12_pf4", - "lcd_d13_pf5", - "lcd_d14_pf6", - "lcd_d15_pf7", - "lcd_d16_pm0", - "lcd_d17_pm1", - "lcd_d18_pm2", - "lcd_d19_pm3", - "lcd_d2_pe2", - "lcd_d20_pm4", - "lcd_d21_pm5", - "lcd_d22_pm6", - "lcd_d23_pm7", - "lcd_d3_pe3", - "lcd_d4_pe4", - "lcd_d5_pe5", - "lcd_d6_pe6", - "lcd_d7_pe7", - "lcd_d8_pf0", - "lcd_d9_pf1", - "lcd_dc0_pn6", - "lcd_dc1_pd2", - "lcd_de_pj1", - "lcd_hsync_pj3", - "lcd_m1_pw1", - "lcd_pclk_pb3", - "lcd_pwr1_pc1", - "lcd_vsync_pj4", - "owr", - "pex_l0_clkreq_n_pdd2", - "pex_l0_prsnt_n_pdd0", - "pex_l0_rst_n_pdd1", - "pex_l1_clkreq_n_pdd6", - "pex_l1_prsnt_n_pdd4", - "pex_l1_rst_n_pdd5", - "pex_l2_clkreq_n_pcc7", - "pex_l2_prsnt_n_pdd7", - "pex_l2_rst_n_pcc6", - "pex_wake_n_pdd3", - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", - "pwr_int_n", - "sdmmc1_clk_pz0", - "sdmmc1_cmd_pz1", - "sdmmc4_rst_n_pcc3", - "sys_clk_req_pz5", -}; - -static const char * const rsvd4_groups[] = { - "clk1_out_pw4", - "clk1_req_pee2", - "clk2_out_pw5", - "clk2_req_pcc5", - "clk3_out_pee0", - "clk3_req_pee1", - "clk_32k_in", - "clk_32k_out_pa0", - "core_pwr_req", - "cpu_pwr_req", - "crt_hsync_pv6", - "crt_vsync_pv7", - "dap4_din_pp5", - "dap4_dout_pp6", - "dap4_fs_pp4", - "dap4_sclk_pp7", - "ddc_scl_pv4", - "ddc_sda_pv5", - "gen1_i2c_scl_pc4", - "gen1_i2c_sda_pc5", - "gen2_i2c_scl_pt5", - "gen2_i2c_sda_pt6", - "gmi_a19_pk7", - "gmi_ad0_pg0", - "gmi_ad1_pg1", - "gmi_ad10_ph2", - "gmi_ad11_ph3", - "gmi_ad12_ph4", - "gmi_ad13_ph5", - "gmi_ad14_ph6", - "gmi_ad15_ph7", - "gmi_ad2_pg2", - "gmi_ad3_pg3", - "gmi_ad4_pg4", - "gmi_ad5_pg5", - "gmi_ad6_pg6", - "gmi_ad7_pg7", - "gmi_ad8_ph0", - "gmi_ad9_ph1", - "gmi_adv_n_pk0", - "gmi_clk_pk1", - "gmi_cs2_n_pk3", - "gmi_cs4_n_pk2", - "gmi_dqs_pi2", - "gmi_iordy_pi5", - "gmi_oe_n_pi1", - "gmi_rst_n_pi4", - "gmi_wait_pi7", - "gmi_wr_n_pi0", - "pcc2", - "pu0", - "pu1", - "pu2", - "pu3", - "pu4", - "pu5", - "pu6", - "pv0", - "pv1", - "pv2", - "pv3", - "hdmi_cec_pee3", - "hdmi_int_pn7", - "jtag_rtck_pu7", - "kb_col2_pq2", - "kb_col3_pq3", - "kb_col4_pq4", - "kb_col5_pq5", - "kb_row0_pr0", - "kb_row1_pr1", - "kb_row2_pr2", - "kb_row4_pr4", - "lcd_cs0_n_pn4", - "lcd_cs1_n_pw0", - "lcd_d0_pe0", - "lcd_d1_pe1", - "lcd_d10_pf2", - "lcd_d11_pf3", - "lcd_d12_pf4", - "lcd_d13_pf5", - "lcd_d14_pf6", - "lcd_d15_pf7", - "lcd_d16_pm0", - "lcd_d17_pm1", - "lcd_d18_pm2", - "lcd_d19_pm3", - "lcd_d2_pe2", - "lcd_d20_pm4", - "lcd_d21_pm5", - "lcd_d22_pm6", - "lcd_d23_pm7", - "lcd_d3_pe3", - "lcd_d4_pe4", - "lcd_d5_pe5", - "lcd_d6_pe6", - "lcd_d7_pe7", - "lcd_d8_pf0", - "lcd_d9_pf1", - "lcd_dc0_pn6", - "lcd_dc1_pd2", - "lcd_de_pj1", - "lcd_hsync_pj3", - "lcd_m1_pw1", - "lcd_pclk_pb3", - "lcd_pwr1_pc1", - "lcd_sdin_pz2", - "lcd_vsync_pj4", - "owr", - "pex_l0_clkreq_n_pdd2", - "pex_l0_prsnt_n_pdd0", - "pex_l0_rst_n_pdd1", - "pex_l1_clkreq_n_pdd6", - "pex_l1_prsnt_n_pdd4", - "pex_l1_rst_n_pdd5", - "pex_l2_clkreq_n_pcc7", - "pex_l2_prsnt_n_pdd7", - "pex_l2_rst_n_pcc6", - "pex_wake_n_pdd3", - "pwr_i2c_scl_pz6", - "pwr_i2c_sda_pz7", - "pwr_int_n", - "spi1_miso_px7", - "sys_clk_req_pz5", - "uart3_cts_n_pa1", - "uart3_rts_n_pc0", - "uart3_rxd_pw7", - "uart3_txd_pw6", - "vi_d0_pt4", - "vi_d1_pd5", - "vi_d10_pt2", - "vi_d11_pt3", - "vi_d2_pl0", - "vi_d3_pl1", - "vi_d4_pl2", - "vi_d5_pl3", - "vi_d6_pl4", - "vi_d7_pl5", - "vi_d8_pl6", - "vi_d9_pl7", - "vi_hsync_pd7", - "vi_pclk_pt0", - "vi_vsync_pd6", -}; - -static const char * const rtck_groups[] = { - "jtag_rtck_pu7", -}; - -static const char * const sata_groups[] = { - "gmi_cs6_n_pi3", -}; - -static const char * const sdmmc1_groups[] = { - "sdmmc1_clk_pz0", - "sdmmc1_cmd_pz1", - "sdmmc1_dat0_py7", - "sdmmc1_dat1_py6", - "sdmmc1_dat2_py5", - "sdmmc1_dat3_py4", -}; - -static const char * const sdmmc2_groups[] = { - "dap1_din_pn1", - "dap1_dout_pn2", - "dap1_fs_pn0", - "dap1_sclk_pn3", - "kb_row10_ps2", - "kb_row11_ps3", - "kb_row12_ps4", - "kb_row13_ps5", - "kb_row14_ps6", - "kb_row15_ps7", - "kb_row6_pr6", - "kb_row7_pr7", - "kb_row8_ps0", - "kb_row9_ps1", - "spdif_in_pk6", - "spdif_out_pk5", - "vi_d1_pd5", - "vi_d2_pl0", - "vi_d3_pl1", - "vi_d4_pl2", - "vi_d5_pl3", - "vi_d6_pl4", - "vi_d7_pl5", - "vi_d8_pl6", - "vi_d9_pl7", - "vi_pclk_pt0", -}; - -static const char * const sdmmc3_groups[] = { - "sdmmc3_clk_pa6", - "sdmmc3_cmd_pa7", - "sdmmc3_dat0_pb7", - "sdmmc3_dat1_pb6", - "sdmmc3_dat2_pb5", - "sdmmc3_dat3_pb4", - "sdmmc3_dat4_pd1", - "sdmmc3_dat5_pd0", - "sdmmc3_dat6_pd3", - "sdmmc3_dat7_pd4", -}; - -static const char * const sdmmc4_groups[] = { - "cam_i2c_scl_pbb1", - "cam_i2c_sda_pbb2", - "cam_mclk_pcc0", - "pbb0", - "pbb3", - "pbb4", - "pbb5", - "pbb6", - "pbb7", - "pcc1", - "sdmmc4_clk_pcc4", - "sdmmc4_cmd_pt7", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "sdmmc4_dat4_paa4", - "sdmmc4_dat5_paa5", - "sdmmc4_dat6_paa6", - "sdmmc4_dat7_paa7", - "sdmmc4_rst_n_pcc3", -}; - -static const char * const spdif_groups[] = { - "sdmmc3_dat6_pd3", - "sdmmc3_dat7_pd4", - "spdif_in_pk6", - "spdif_out_pk5", - "uart2_rxd_pc3", - "uart2_txd_pc2", -}; - -static const char * const spi1_groups[] = { - "spi1_cs0_n_px6", - "spi1_miso_px7", - "spi1_mosi_px4", - "spi1_sck_px5", - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", -}; - -static const char * const spi2_groups[] = { - "sdmmc3_cmd_pa7", - "sdmmc3_dat4_pd1", - "sdmmc3_dat5_pd0", - "sdmmc3_dat6_pd3", - "sdmmc3_dat7_pd4", - "spi1_cs0_n_px6", - "spi1_mosi_px4", - "spi1_sck_px5", - "spi2_cs0_n_px3", - "spi2_cs1_n_pw2", - "spi2_cs2_n_pw3", - "spi2_miso_px1", - "spi2_mosi_px0", - "spi2_sck_px2", - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", -}; - -static const char * const spi2_alt_groups[] = { - "spi1_cs0_n_px6", - "spi1_miso_px7", - "spi1_mosi_px4", - "spi1_sck_px5", - "spi2_cs1_n_pw2", - "spi2_cs2_n_pw3", -}; - -static const char * const spi3_groups[] = { - "sdmmc3_clk_pa6", - "sdmmc3_dat0_pb7", - "sdmmc3_dat1_pb6", - "sdmmc3_dat2_pb5", - "sdmmc3_dat3_pb4", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", - "spi1_miso_px7", - "spi2_cs0_n_px3", - "spi2_cs1_n_pw2", - "spi2_cs2_n_pw3", - "spi2_miso_px1", - "spi2_mosi_px0", - "spi2_sck_px2", - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", -}; - -static const char * const spi4_groups[] = { - "gmi_a16_pj7", - "gmi_a17_pb0", - "gmi_a18_pb1", - "gmi_a19_pk7", - "sdmmc3_dat4_pd1", - "sdmmc3_dat5_pd0", - "sdmmc3_dat6_pd3", - "sdmmc3_dat7_pd4", - "uart2_cts_n_pj5", - "uart2_rts_n_pj6", - "uart2_rxd_pc3", - "uart2_txd_pc2", -}; - -static const char * const spi5_groups[] = { - "lcd_cs0_n_pn4", - "lcd_cs1_n_pw0", - "lcd_pwr0_pb2", - "lcd_pwr2_pc6", - "lcd_sck_pz4", - "lcd_sdin_pz2", - "lcd_sdout_pn5", - "lcd_wr_n_pz3", -}; - -static const char * const spi6_groups[] = { - "spi2_cs0_n_px3", - "spi2_miso_px1", - "spi2_mosi_px0", - "spi2_sck_px2", -}; - -static const char * const sysclk_groups[] = { - "sys_clk_req_pz5", -}; - -static const char * const test_groups[] = { - "kb_col0_pq0", - "kb_col1_pq1", -}; - -static const char * const trace_groups[] = { - "kb_col0_pq0", - "kb_col1_pq1", - "kb_col2_pq2", - "kb_col3_pq3", - "kb_col4_pq4", - "kb_col5_pq5", - "kb_col6_pq6", - "kb_col7_pq7", - "kb_row4_pr4", - "kb_row5_pr5", -}; - -static const char * const uarta_groups[] = { - "pu0", - "pu1", - "pu2", - "pu3", - "pu4", - "pu5", - "pu6", - "sdmmc1_clk_pz0", - "sdmmc1_cmd_pz1", - "sdmmc1_dat0_py7", - "sdmmc1_dat1_py6", - "sdmmc1_dat2_py5", - "sdmmc1_dat3_py4", - "sdmmc3_clk_pa6", - "sdmmc3_cmd_pa7", - "uart2_cts_n_pj5", - "uart2_rts_n_pj6", - "uart2_rxd_pc3", - "uart2_txd_pc2", - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", -}; - -static const char * const uartb_groups[] = { - "uart2_cts_n_pj5", - "uart2_rts_n_pj6", - "uart2_rxd_pc3", - "uart2_txd_pc2", -}; - -static const char * const uartc_groups[] = { - "uart3_cts_n_pa1", - "uart3_rts_n_pc0", - "uart3_rxd_pw7", - "uart3_txd_pw6", -}; - -static const char * const uartd_groups[] = { - "gmi_a16_pj7", - "gmi_a17_pb0", - "gmi_a18_pb1", - "gmi_a19_pk7", - "ulpi_clk_py0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", -}; - -static const char * const uarte_groups[] = { - "sdmmc1_dat0_py7", - "sdmmc1_dat1_py6", - "sdmmc1_dat2_py5", - "sdmmc1_dat3_py4", - "sdmmc4_dat0_paa0", - "sdmmc4_dat1_paa1", - "sdmmc4_dat2_paa2", - "sdmmc4_dat3_paa3", -}; - -static const char * const ulpi_groups[] = { - "ulpi_clk_py0", - "ulpi_data0_po1", - "ulpi_data1_po2", - "ulpi_data2_po3", - "ulpi_data3_po4", - "ulpi_data4_po5", - "ulpi_data5_po6", - "ulpi_data6_po7", - "ulpi_data7_po0", - "ulpi_dir_py1", - "ulpi_nxt_py2", - "ulpi_stp_py3", -}; - -static const char * const vgp1_groups[] = { - "cam_i2c_scl_pbb1", -}; - -static const char * const vgp2_groups[] = { - "cam_i2c_sda_pbb2", -}; - -static const char * const vgp3_groups[] = { - "pbb3", - "sdmmc4_dat5_paa5", -}; - -static const char * const vgp4_groups[] = { - "pbb4", - "sdmmc4_dat6_paa6", -}; - -static const char * const vgp5_groups[] = { - "pbb5", - "sdmmc4_dat7_paa7", -}; - -static const char * const vgp6_groups[] = { - "pbb6", - "sdmmc4_rst_n_pcc3", -}; - -static const char * const vi_groups[] = { - "cam_mclk_pcc0", - "vi_d0_pt4", - "vi_d1_pd5", - "vi_d10_pt2", - "vi_d11_pt3", - "vi_d2_pl0", - "vi_d3_pl1", - "vi_d4_pl2", - "vi_d5_pl3", - "vi_d6_pl4", - "vi_d7_pl5", - "vi_d8_pl6", - "vi_d9_pl7", - "vi_hsync_pd7", - "vi_mclk_pt1", - "vi_pclk_pt0", - "vi_vsync_pd6", -}; - -static const char * const vi_alt1_groups[] = { - "cam_mclk_pcc0", - "vi_mclk_pt1", -}; - -static const char * const vi_alt2_groups[] = { - "vi_mclk_pt1", -}; - -static const char * const vi_alt3_groups[] = { - "cam_mclk_pcc0", - "vi_mclk_pt1", -}; #define FUNCTION(fname) \ { \ .name = #fname, \ - .groups = fname##_groups, \ - .ngroups = ARRAY_SIZE(fname##_groups), \ } -static const struct tegra_function tegra30_functions[] = { +static struct tegra_function tegra30_functions[] = { FUNCTION(blink), FUNCTION(cec), FUNCTION(clk_12m_out), @@ -3345,11 +2105,11 @@ static const struct tegra_function tegra30_functions[] = { FUNCTION(vi_alt3), }; -#define DRV_PINGROUP_REG_A 0x868 /* bank 0 */ -#define PINGROUP_REG_A 0x3000 /* bank 1 */ +#define DRV_PINGROUP_REG_A 0x868 /* bank 0 */ +#define PINGROUP_REG_A 0x3000 /* bank 1 */ -#define PINGROUP_REG_Y(r) ((r) - PINGROUP_REG_A) -#define PINGROUP_REG_N(r) -1 +#define PINGROUP_REG_Y(r) ((r) - PINGROUP_REG_A) +#define PINGROUP_REG_N(r) -1 #define PINGROUP(pg_name, f0, f1, f2, f3, f_safe, r, od, ior) \ { \ @@ -3357,12 +2117,12 @@ static const struct tegra_function tegra30_functions[] = { .pins = pg_name##_pins, \ .npins = ARRAY_SIZE(pg_name##_pins), \ .funcs = { \ - TEGRA_MUX_ ## f0, \ - TEGRA_MUX_ ## f1, \ - TEGRA_MUX_ ## f2, \ - TEGRA_MUX_ ## f3, \ + TEGRA_MUX_##f0, \ + TEGRA_MUX_##f1, \ + TEGRA_MUX_##f2, \ + TEGRA_MUX_##f3, \ }, \ - .func_safe = TEGRA_MUX_ ## f_safe, \ + .func_safe = TEGRA_MUX_##f_safe, \ .mux_reg = PINGROUP_REG_Y(r), \ .mux_bank = 1, \ .mux_bit = 0, \ @@ -3389,6 +2149,9 @@ static const struct tegra_function tegra30_functions[] = { .drvtype_reg = -1, \ } +#define DRV_PINGROUP_REG_Y(r) ((r) - DRV_PINGROUP_REG_A) +#define DRV_PINGROUP_REG_N(r) -1 + #define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, \ drvdn_b, drvdn_w, drvup_b, drvup_w, \ slwr_b, slwr_w, slwf_b, slwf_w) \ @@ -3404,7 +2167,7 @@ static const struct tegra_function tegra30_functions[] = { .lock_reg = -1, \ .ioreset_reg = -1, \ .rcv_sel_reg = -1, \ - .drv_reg = ((r) - DRV_PINGROUP_REG_A), \ + .drv_reg = DRV_PINGROUP_REG_Y(r), \ .drv_bank = 0, \ .hsm_bit = hsm_b, \ .schmitt_bit = schmitt_b, \ @@ -3422,7 +2185,6 @@ static const struct tegra_function tegra30_functions[] = { static const struct tegra_pingroup tegra30_groups[] = { /* pg_name, f0, f1, f2, f3, safe, r, od, ior */ - /* FIXME: Fill in correct data in safe column */ PINGROUP(clk_32k_out_pa0, BLINK, RSVD2, RSVD3, RSVD4, RSVD4, 0x331c, N, N), PINGROUP(uart3_cts_n_pa1, UARTC, RSVD2, GMI, RSVD4, RSVD4, 0x317c, N, N), PINGROUP(dap2_fs_pa2, I2S1, HDA, RSVD3, GMI, RSVD3, 0x3358, N, N), @@ -3735,6 +2497,7 @@ static struct of_device_id tegra30_pinctrl_of_match[] = { { .compatible = "nvidia,tegra30-pinmux", }, { }, }; +MODULE_DEVICE_TABLE(of, tegra30_pinctrl_of_match); static struct platform_driver tegra30_pinctrl_driver = { .driver = { @@ -3745,20 +2508,8 @@ static struct platform_driver tegra30_pinctrl_driver = { .probe = tegra30_pinctrl_probe, .remove = tegra_pinctrl_remove, }; - -static int __init tegra30_pinctrl_init(void) -{ - return platform_driver_register(&tegra30_pinctrl_driver); -} -arch_initcall(tegra30_pinctrl_init); - -static void __exit tegra30_pinctrl_exit(void) -{ - platform_driver_unregister(&tegra30_pinctrl_driver); -} -module_exit(tegra30_pinctrl_exit); +module_platform_driver(tegra30_pinctrl_driver); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("NVIDIA Tegra30 pinctrl driver"); MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, tegra30_pinctrl_of_match); diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c index c381ae63c508..48093719167a 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c @@ -2260,6 +2260,42 @@ static const unsigned int msiof0_tx_pins[] = { static const unsigned int msiof0_tx_mux[] = { MSIOF0_TXD_MARK, }; + +static const unsigned int msiof0_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(1, 23), +}; +static const unsigned int msiof0_clk_b_mux[] = { + MSIOF0_SCK_B_MARK, +}; +static const unsigned int msiof0_ss1_b_pins[] = { + /* SS1 */ + RCAR_GP_PIN(1, 12), +}; +static const unsigned int msiof0_ss1_b_mux[] = { + MSIOF0_SS1_B_MARK, +}; +static const unsigned int msiof0_ss2_b_pins[] = { + /* SS2 */ + RCAR_GP_PIN(1, 10), +}; +static const unsigned int msiof0_ss2_b_mux[] = { + MSIOF0_SS2_B_MARK, +}; +static const unsigned int msiof0_rx_b_pins[] = { + /* RXD */ + RCAR_GP_PIN(1, 29), +}; +static const unsigned int msiof0_rx_b_mux[] = { + MSIOF0_RXD_B_MARK, +}; +static const unsigned int msiof0_tx_b_pins[] = { + /* TXD */ + RCAR_GP_PIN(1, 28), +}; +static const unsigned int msiof0_tx_b_mux[] = { + MSIOF0_TXD_B_MARK, +}; /* - MSIOF1 ----------------------------------------------------------------- */ static const unsigned int msiof1_clk_pins[] = { /* SCK */ @@ -2303,6 +2339,42 @@ static const unsigned int msiof1_tx_pins[] = { static const unsigned int msiof1_tx_mux[] = { MSIOF1_TXD_MARK, }; + +static const unsigned int msiof1_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(1, 16), +}; +static const unsigned int msiof1_clk_b_mux[] = { + MSIOF1_SCK_B_MARK, +}; +static const unsigned int msiof1_ss1_b_pins[] = { + /* SS1 */ + RCAR_GP_PIN(0, 18), +}; +static const unsigned int msiof1_ss1_b_mux[] = { + MSIOF1_SS1_B_MARK, +}; +static const unsigned int msiof1_ss2_b_pins[] = { + /* SS2 */ + RCAR_GP_PIN(0, 19), +}; +static const unsigned int msiof1_ss2_b_mux[] = { + MSIOF1_SS2_B_MARK, +}; +static const unsigned int msiof1_rx_b_pins[] = { + /* RXD */ + RCAR_GP_PIN(1, 17), +}; +static const unsigned int msiof1_rx_b_mux[] = { + MSIOF1_RXD_B_MARK, +}; +static const unsigned int msiof1_tx_b_pins[] = { + /* TXD */ + RCAR_GP_PIN(0, 20), +}; +static const unsigned int msiof1_tx_b_mux[] = { + MSIOF1_TXD_B_MARK, +}; /* - MSIOF2 ----------------------------------------------------------------- */ static const unsigned int msiof2_clk_pins[] = { /* SCK */ @@ -2389,6 +2461,58 @@ static const unsigned int msiof3_tx_pins[] = { static const unsigned int msiof3_tx_mux[] = { MSIOF3_TXD_MARK, }; + +static const unsigned int msiof3_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(0, 0), +}; +static const unsigned int msiof3_clk_b_mux[] = { + MSIOF3_SCK_B_MARK, +}; +static const unsigned int msiof3_sync_b_pins[] = { + /* SYNC */ + RCAR_GP_PIN(0, 1), +}; +static const unsigned int msiof3_sync_b_mux[] = { + MSIOF3_SYNC_B_MARK, +}; +static const unsigned int msiof3_rx_b_pins[] = { + /* RXD */ + RCAR_GP_PIN(0, 2), +}; +static const unsigned int msiof3_rx_b_mux[] = { + MSIOF3_RXD_B_MARK, +}; +static const unsigned int msiof3_tx_b_pins[] = { + /* TXD */ + RCAR_GP_PIN(0, 3), +}; +static const unsigned int msiof3_tx_b_mux[] = { + MSIOF3_TXD_B_MARK, +}; +/* - QSPI ------------------------------------------------------------------- */ +static const unsigned int qspi_ctrl_pins[] = { + /* SPCLK, SSL */ + RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 9), +}; +static const unsigned int qspi_ctrl_mux[] = { + SPCLK_MARK, SSL_MARK, +}; +static const unsigned int qspi_data2_pins[] = { + /* MOSI_IO0, MISO_IO1 */ + RCAR_GP_PIN(1, 5), RCAR_GP_PIN(1, 6), +}; +static const unsigned int qspi_data2_mux[] = { + MOSI_IO0_MARK, MISO_IO1_MARK, +}; +static const unsigned int qspi_data4_pins[] = { + /* MOSI_IO0, MISO_IO1, IO2, IO3 */ + RCAR_GP_PIN(1, 5), RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7), + RCAR_GP_PIN(1, 8), +}; +static const unsigned int qspi_data4_mux[] = { + MOSI_IO0_MARK, MISO_IO1_MARK, IO2_MARK, IO3_MARK, +}; /* - SCIF0 ------------------------------------------------------------------ */ static const unsigned int scif0_data_pins[] = { /* RX, TX */ @@ -3231,6 +3355,13 @@ static const unsigned int usb0_pins[] = { static const unsigned int usb0_mux[] = { USB0_PWEN_MARK, USB0_OVC_VBUS_MARK, }; +static const unsigned int usb0_ovc_vbus_pins[] = { + /* OVC/VBUS */ + RCAR_GP_PIN(5, 19), +}; +static const unsigned int usb0_ovc_vbus_mux[] = { + USB0_OVC_VBUS_MARK, +}; /* - USB1 ------------------------------------------------------------------- */ static const unsigned int usb1_pins[] = { /* PWEN, OVC */ @@ -3653,12 +3784,22 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(msiof0_ss2), SH_PFC_PIN_GROUP(msiof0_rx), SH_PFC_PIN_GROUP(msiof0_tx), + SH_PFC_PIN_GROUP(msiof0_clk_b), + SH_PFC_PIN_GROUP(msiof0_ss1_b), + SH_PFC_PIN_GROUP(msiof0_ss2_b), + SH_PFC_PIN_GROUP(msiof0_rx_b), + SH_PFC_PIN_GROUP(msiof0_tx_b), SH_PFC_PIN_GROUP(msiof1_clk), SH_PFC_PIN_GROUP(msiof1_sync), SH_PFC_PIN_GROUP(msiof1_ss1), SH_PFC_PIN_GROUP(msiof1_ss2), SH_PFC_PIN_GROUP(msiof1_rx), SH_PFC_PIN_GROUP(msiof1_tx), + SH_PFC_PIN_GROUP(msiof1_clk_b), + SH_PFC_PIN_GROUP(msiof1_ss1_b), + SH_PFC_PIN_GROUP(msiof1_ss2_b), + SH_PFC_PIN_GROUP(msiof1_rx_b), + SH_PFC_PIN_GROUP(msiof1_tx_b), SH_PFC_PIN_GROUP(msiof2_clk), SH_PFC_PIN_GROUP(msiof2_sync), SH_PFC_PIN_GROUP(msiof2_ss1), @@ -3671,6 +3812,13 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(msiof3_ss2), SH_PFC_PIN_GROUP(msiof3_rx), SH_PFC_PIN_GROUP(msiof3_tx), + SH_PFC_PIN_GROUP(msiof3_clk_b), + SH_PFC_PIN_GROUP(msiof3_sync_b), + SH_PFC_PIN_GROUP(msiof3_rx_b), + SH_PFC_PIN_GROUP(msiof3_tx_b), + SH_PFC_PIN_GROUP(qspi_ctrl), + SH_PFC_PIN_GROUP(qspi_data2), + SH_PFC_PIN_GROUP(qspi_data4), SH_PFC_PIN_GROUP(scif0_data), SH_PFC_PIN_GROUP(scif0_clk), SH_PFC_PIN_GROUP(scif0_ctrl), @@ -3789,6 +3937,7 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(tpu0_to2), SH_PFC_PIN_GROUP(tpu0_to3), SH_PFC_PIN_GROUP(usb0), + SH_PFC_PIN_GROUP(usb0_ovc_vbus), SH_PFC_PIN_GROUP(usb1), SH_PFC_PIN_GROUP(usb2), VIN_DATA_PIN_GROUP(vin0_data, 24), @@ -3941,6 +4090,11 @@ static const char * const msiof0_groups[] = { "msiof0_ss2", "msiof0_rx", "msiof0_tx", + "msiof0_clk_b", + "msiof0_ss1_b", + "msiof0_ss2_b", + "msiof0_rx_b", + "msiof0_tx_b", }; static const char * const msiof1_groups[] = { @@ -3950,6 +4104,11 @@ static const char * const msiof1_groups[] = { "msiof1_ss2", "msiof1_rx", "msiof1_tx", + "msiof1_clk_b", + "msiof1_ss1_b", + "msiof1_ss2_b", + "msiof1_rx_b", + "msiof1_tx_b", }; static const char * const msiof2_groups[] = { @@ -3968,6 +4127,16 @@ static const char * const msiof3_groups[] = { "msiof3_ss2", "msiof3_rx", "msiof3_tx", + "msiof3_clk_b", + "msiof3_sync_b", + "msiof3_rx_b", + "msiof3_tx_b", +}; + +static const char * const qspi_groups[] = { + "qspi_ctrl", + "qspi_data2", + "qspi_data4", }; static const char * const scif0_groups[] = { @@ -4134,6 +4303,7 @@ static const char * const tpu0_groups[] = { static const char * const usb0_groups[] = { "usb0", + "usb0_ovc_vbus", }; static const char * const usb1_groups[] = { @@ -4213,6 +4383,7 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(msiof1), SH_PFC_FUNCTION(msiof2), SH_PFC_FUNCTION(msiof3), + SH_PFC_FUNCTION(qspi), SH_PFC_FUNCTION(scif0), SH_PFC_FUNCTION(scif1), SH_PFC_FUNCTION(scif2), diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c index 77d103fe39d9..5186d70c49d4 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7791.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7791.c @@ -89,7 +89,8 @@ enum { /* GPSR6 */ FN_IP13_10, FN_IP13_11, FN_IP13_12, FN_IP13_13, FN_IP13_14, - FN_IP13_15, FN_IP13_18_16, FN_IP13_21_19, FN_IP13_22, FN_IP13_24_23, + FN_IP13_15, FN_IP13_18_16, FN_IP13_21_19, + FN_IP13_22, FN_IP13_24_23, FN_SD1_CLK, FN_IP13_25, FN_IP13_26, FN_IP13_27, FN_IP13_30_28, FN_IP14_1_0, FN_IP14_2, FN_IP14_3, FN_IP14_4, FN_IP14_5, FN_IP14_6, FN_IP14_7, FN_IP14_10_8, FN_IP14_13_11, FN_IP14_16_14, FN_IP14_19_17, @@ -788,6 +789,7 @@ static const u16 pinmux_data[] = { PINMUX_DATA(USB1_PWEN_MARK, FN_USB1_PWEN), PINMUX_DATA(USB1_OVC_MARK, FN_USB1_OVC), PINMUX_DATA(DU0_DOTCLKIN_MARK, FN_DU0_DOTCLKIN), + PINMUX_DATA(SD1_CLK_MARK, FN_SD1_CLK), /* IPSR0 */ PINMUX_IPSR_DATA(IP0_0, D0), @@ -1943,6 +1945,50 @@ static const unsigned int i2c4_c_pins[] = { static const unsigned int i2c4_c_mux[] = { SCL4_C_MARK, SDA4_C_MARK, }; +/* - I2C7 ------------------------------------------------------------------- */ +static const unsigned int i2c7_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(5, 15), RCAR_GP_PIN(5, 16), +}; +static const unsigned int i2c7_mux[] = { + SCL7_MARK, SDA7_MARK, +}; +static const unsigned int i2c7_b_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(2, 2), RCAR_GP_PIN(2, 3), +}; +static const unsigned int i2c7_b_mux[] = { + SCL7_B_MARK, SDA7_B_MARK, +}; +static const unsigned int i2c7_c_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(6, 28), RCAR_GP_PIN(6, 29), +}; +static const unsigned int i2c7_c_mux[] = { + SCL7_C_MARK, SDA7_C_MARK, +}; +/* - I2C8 ------------------------------------------------------------------- */ +static const unsigned int i2c8_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(4, 13), RCAR_GP_PIN(4, 14), +}; +static const unsigned int i2c8_mux[] = { + SCL8_MARK, SDA8_MARK, +}; +static const unsigned int i2c8_b_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(2, 4), RCAR_GP_PIN(2, 5), +}; +static const unsigned int i2c8_b_mux[] = { + SCL8_B_MARK, SDA8_B_MARK, +}; +static const unsigned int i2c8_c_pins[] = { + /* SCL, SDA */ + RCAR_GP_PIN(6, 22), RCAR_GP_PIN(6, 23), +}; +static const unsigned int i2c8_c_mux[] = { + SCL8_C_MARK, SDA8_C_MARK, +}; /* - INTC ------------------------------------------------------------------- */ static const unsigned int intc_irq0_pins[] = { /* IRQ */ @@ -2049,6 +2095,92 @@ static const unsigned int msiof0_tx_pins[] = { static const unsigned int msiof0_tx_mux[] = { MSIOF0_TXD_MARK, }; + +static const unsigned int msiof0_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(0, 16), +}; +static const unsigned int msiof0_clk_b_mux[] = { + MSIOF0_SCK_B_MARK, +}; +static const unsigned int msiof0_sync_b_pins[] = { + /* SYNC */ + RCAR_GP_PIN(0, 17), +}; +static const unsigned int msiof0_sync_b_mux[] = { + MSIOF0_SYNC_B_MARK, +}; +static const unsigned int msiof0_ss1_b_pins[] = { + /* SS1 */ + RCAR_GP_PIN(0, 18), +}; +static const unsigned int msiof0_ss1_b_mux[] = { + MSIOF0_SS1_B_MARK, +}; +static const unsigned int msiof0_ss2_b_pins[] = { + /* SS2 */ + RCAR_GP_PIN(0, 19), +}; +static const unsigned int msiof0_ss2_b_mux[] = { + MSIOF0_SS2_B_MARK, +}; +static const unsigned int msiof0_rx_b_pins[] = { + /* RXD */ + RCAR_GP_PIN(0, 21), +}; +static const unsigned int msiof0_rx_b_mux[] = { + MSIOF0_RXD_B_MARK, +}; +static const unsigned int msiof0_tx_b_pins[] = { + /* TXD */ + RCAR_GP_PIN(0, 20), +}; +static const unsigned int msiof0_tx_b_mux[] = { + MSIOF0_TXD_B_MARK, +}; + +static const unsigned int msiof0_clk_c_pins[] = { + /* SCK */ + RCAR_GP_PIN(5, 26), +}; +static const unsigned int msiof0_clk_c_mux[] = { + MSIOF0_SCK_C_MARK, +}; +static const unsigned int msiof0_sync_c_pins[] = { + /* SYNC */ + RCAR_GP_PIN(5, 25), +}; +static const unsigned int msiof0_sync_c_mux[] = { + MSIOF0_SYNC_C_MARK, +}; +static const unsigned int msiof0_ss1_c_pins[] = { + /* SS1 */ + RCAR_GP_PIN(5, 27), +}; +static const unsigned int msiof0_ss1_c_mux[] = { + MSIOF0_SS1_C_MARK, +}; +static const unsigned int msiof0_ss2_c_pins[] = { + /* SS2 */ + RCAR_GP_PIN(5, 28), +}; +static const unsigned int msiof0_ss2_c_mux[] = { + MSIOF0_SS2_C_MARK, +}; +static const unsigned int msiof0_rx_c_pins[] = { + /* RXD */ + RCAR_GP_PIN(5, 29), +}; +static const unsigned int msiof0_rx_c_mux[] = { + MSIOF0_RXD_C_MARK, +}; +static const unsigned int msiof0_tx_c_pins[] = { + /* TXD */ + RCAR_GP_PIN(5, 30), +}; +static const unsigned int msiof0_tx_c_mux[] = { + MSIOF0_TXD_C_MARK, +}; /* - MSIOF1 ----------------------------------------------------------------- */ static const unsigned int msiof1_clk_pins[] = { /* SCK */ @@ -2092,6 +2224,143 @@ static const unsigned int msiof1_tx_pins[] = { static const unsigned int msiof1_tx_mux[] = { MSIOF1_TXD_MARK, }; + +static const unsigned int msiof1_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(2, 29), +}; +static const unsigned int msiof1_clk_b_mux[] = { + MSIOF1_SCK_B_MARK, +}; +static const unsigned int msiof1_sync_b_pins[] = { + /* SYNC */ + RCAR_GP_PIN(2, 30), +}; +static const unsigned int msiof1_sync_b_mux[] = { + MSIOF1_SYNC_B_MARK, +}; +static const unsigned int msiof1_ss1_b_pins[] = { + /* SS1 */ + RCAR_GP_PIN(2, 31), +}; +static const unsigned int msiof1_ss1_b_mux[] = { + MSIOF1_SS1_B_MARK, +}; +static const unsigned int msiof1_ss2_b_pins[] = { + /* SS2 */ + RCAR_GP_PIN(7, 16), +}; +static const unsigned int msiof1_ss2_b_mux[] = { + MSIOF1_SS2_B_MARK, +}; +static const unsigned int msiof1_rx_b_pins[] = { + /* RXD */ + RCAR_GP_PIN(7, 18), +}; +static const unsigned int msiof1_rx_b_mux[] = { + MSIOF1_RXD_B_MARK, +}; +static const unsigned int msiof1_tx_b_pins[] = { + /* TXD */ + RCAR_GP_PIN(7, 17), +}; +static const unsigned int msiof1_tx_b_mux[] = { + MSIOF1_TXD_B_MARK, +}; + +static const unsigned int msiof1_clk_c_pins[] = { + /* SCK */ + RCAR_GP_PIN(2, 15), +}; +static const unsigned int msiof1_clk_c_mux[] = { + MSIOF1_SCK_C_MARK, +}; +static const unsigned int msiof1_sync_c_pins[] = { + /* SYNC */ + RCAR_GP_PIN(2, 16), +}; +static const unsigned int msiof1_sync_c_mux[] = { + MSIOF1_SYNC_C_MARK, +}; +static const unsigned int msiof1_rx_c_pins[] = { + /* RXD */ + RCAR_GP_PIN(2, 18), +}; +static const unsigned int msiof1_rx_c_mux[] = { + MSIOF1_RXD_C_MARK, +}; +static const unsigned int msiof1_tx_c_pins[] = { + /* TXD */ + RCAR_GP_PIN(2, 17), +}; +static const unsigned int msiof1_tx_c_mux[] = { + MSIOF1_TXD_C_MARK, +}; + +static const unsigned int msiof1_clk_d_pins[] = { + /* SCK */ + RCAR_GP_PIN(0, 28), +}; +static const unsigned int msiof1_clk_d_mux[] = { + MSIOF1_SCK_D_MARK, +}; +static const unsigned int msiof1_sync_d_pins[] = { + /* SYNC */ + RCAR_GP_PIN(0, 30), +}; +static const unsigned int msiof1_sync_d_mux[] = { + MSIOF1_SYNC_D_MARK, +}; +static const unsigned int msiof1_ss1_d_pins[] = { + /* SS1 */ + RCAR_GP_PIN(0, 29), +}; +static const unsigned int msiof1_ss1_d_mux[] = { + MSIOF1_SS1_D_MARK, +}; +static const unsigned int msiof1_rx_d_pins[] = { + /* RXD */ + RCAR_GP_PIN(0, 27), +}; +static const unsigned int msiof1_rx_d_mux[] = { + MSIOF1_RXD_D_MARK, +}; +static const unsigned int msiof1_tx_d_pins[] = { + /* TXD */ + RCAR_GP_PIN(0, 26), +}; +static const unsigned int msiof1_tx_d_mux[] = { + MSIOF1_TXD_D_MARK, +}; + +static const unsigned int msiof1_clk_e_pins[] = { + /* SCK */ + RCAR_GP_PIN(5, 18), +}; +static const unsigned int msiof1_clk_e_mux[] = { + MSIOF1_SCK_E_MARK, +}; +static const unsigned int msiof1_sync_e_pins[] = { + /* SYNC */ + RCAR_GP_PIN(5, 19), +}; +static const unsigned int msiof1_sync_e_mux[] = { + MSIOF1_SYNC_E_MARK, +}; +static const unsigned int msiof1_rx_e_pins[] = { + /* RXD */ + RCAR_GP_PIN(5, 17), +}; +static const unsigned int msiof1_rx_e_mux[] = { + MSIOF1_RXD_E_MARK, +}; +static const unsigned int msiof1_tx_e_pins[] = { + /* TXD */ + RCAR_GP_PIN(5, 20), +}; +static const unsigned int msiof1_tx_e_mux[] = { + MSIOF1_TXD_E_MARK, +}; /* - MSIOF2 ----------------------------------------------------------------- */ static const unsigned int msiof2_clk_pins[] = { /* SCK */ @@ -2135,6 +2404,197 @@ static const unsigned int msiof2_tx_pins[] = { static const unsigned int msiof2_tx_mux[] = { MSIOF2_TXD_MARK, }; + +static const unsigned int msiof2_clk_b_pins[] = { + /* SCK */ + RCAR_GP_PIN(3, 0), +}; +static const unsigned int msiof2_clk_b_mux[] = { + MSIOF2_SCK_B_MARK, +}; +static const unsigned int msiof2_sync_b_pins[] = { + /* SYNC */ + RCAR_GP_PIN(3, 1), +}; +static const unsigned int msiof2_sync_b_mux[] = { + MSIOF2_SYNC_B_MARK, +}; +static const unsigned int msiof2_ss1_b_pins[] = { + /* SS1 */ + RCAR_GP_PIN(3, 8), +}; +static const unsigned int msiof2_ss1_b_mux[] = { + MSIOF2_SS1_B_MARK, +}; +static const unsigned int msiof2_ss2_b_pins[] = { + /* SS2 */ + RCAR_GP_PIN(3, 9), +}; +static const unsigned int msiof2_ss2_b_mux[] = { + MSIOF2_SS2_B_MARK, +}; +static const unsigned int msiof2_rx_b_pins[] = { + /* RXD */ + RCAR_GP_PIN(3, 17), +}; +static const unsigned int msiof2_rx_b_mux[] = { + MSIOF2_RXD_B_MARK, +}; +static const unsigned int msiof2_tx_b_pins[] = { + /* TXD */ + RCAR_GP_PIN(3, 16), +}; +static const unsigned int msiof2_tx_b_mux[] = { + MSIOF2_TXD_B_MARK, +}; + +static const unsigned int msiof2_clk_c_pins[] = { + /* SCK */ + RCAR_GP_PIN(2, 2), +}; +static const unsigned int msiof2_clk_c_mux[] = { + MSIOF2_SCK_C_MARK, +}; +static const unsigned int msiof2_sync_c_pins[] = { + /* SYNC */ + RCAR_GP_PIN(2, 3), +}; +static const unsigned int msiof2_sync_c_mux[] = { + MSIOF2_SYNC_C_MARK, +}; +static const unsigned int msiof2_rx_c_pins[] = { + /* RXD */ + RCAR_GP_PIN(2, 5), +}; +static const unsigned int msiof2_rx_c_mux[] = { + MSIOF2_RXD_C_MARK, +}; +static const unsigned int msiof2_tx_c_pins[] = { + /* TXD */ + RCAR_GP_PIN(2, 4), +}; +static const unsigned int msiof2_tx_c_mux[] = { + MSIOF2_TXD_C_MARK, +}; + +static const unsigned int msiof2_clk_d_pins[] = { + /* SCK */ + RCAR_GP_PIN(2, 14), +}; +static const unsigned int msiof2_clk_d_mux[] = { + MSIOF2_SCK_D_MARK, +}; +static const unsigned int msiof2_sync_d_pins[] = { + /* SYNC */ + RCAR_GP_PIN(2, 15), +}; +static const unsigned int msiof2_sync_d_mux[] = { + MSIOF2_SYNC_D_MARK, +}; +static const unsigned int msiof2_ss1_d_pins[] = { + /* SS1 */ + RCAR_GP_PIN(2, 17), +}; +static const unsigned int msiof2_ss1_d_mux[] = { + MSIOF2_SS1_D_MARK, +}; +static const unsigned int msiof2_ss2_d_pins[] = { + /* SS2 */ + RCAR_GP_PIN(2, 19), +}; +static const unsigned int msiof2_ss2_d_mux[] = { + MSIOF2_SS2_D_MARK, +}; +static const unsigned int msiof2_rx_d_pins[] = { + /* RXD */ + RCAR_GP_PIN(2, 18), +}; +static const unsigned int msiof2_rx_d_mux[] = { + MSIOF2_RXD_D_MARK, +}; +static const unsigned int msiof2_tx_d_pins[] = { + /* TXD */ + RCAR_GP_PIN(2, 16), +}; +static const unsigned int msiof2_tx_d_mux[] = { + MSIOF2_TXD_D_MARK, +}; + +static const unsigned int msiof2_clk_e_pins[] = { + /* SCK */ + RCAR_GP_PIN(7, 15), +}; +static const unsigned int msiof2_clk_e_mux[] = { + MSIOF2_SCK_E_MARK, +}; +static const unsigned int msiof2_sync_e_pins[] = { + /* SYNC */ + RCAR_GP_PIN(7, 16), +}; +static const unsigned int msiof2_sync_e_mux[] = { + MSIOF2_SYNC_E_MARK, +}; +static const unsigned int msiof2_rx_e_pins[] = { + /* RXD */ + RCAR_GP_PIN(7, 14), +}; +static const unsigned int msiof2_rx_e_mux[] = { + MSIOF2_RXD_E_MARK, +}; +static const unsigned int msiof2_tx_e_pins[] = { + /* TXD */ + RCAR_GP_PIN(7, 13), +}; +static const unsigned int msiof2_tx_e_mux[] = { + MSIOF2_TXD_E_MARK, +}; +/* - QSPI ------------------------------------------------------------------- */ +static const unsigned int qspi_ctrl_pins[] = { + /* SPCLK, SSL */ + RCAR_GP_PIN(1, 4), RCAR_GP_PIN(1, 9), +}; +static const unsigned int qspi_ctrl_mux[] = { + SPCLK_MARK, SSL_MARK, +}; +static const unsigned int qspi_data2_pins[] = { + /* MOSI_IO0, MISO_IO1 */ + RCAR_GP_PIN(1, 5), RCAR_GP_PIN(1, 6), +}; +static const unsigned int qspi_data2_mux[] = { + MOSI_IO0_MARK, MISO_IO1_MARK, +}; +static const unsigned int qspi_data4_pins[] = { + /* MOSI_IO0, MISO_IO1, IO2, IO3 */ + RCAR_GP_PIN(1, 5), RCAR_GP_PIN(1, 6), RCAR_GP_PIN(1, 7), + RCAR_GP_PIN(1, 8), +}; +static const unsigned int qspi_data4_mux[] = { + MOSI_IO0_MARK, MISO_IO1_MARK, IO2_MARK, IO3_MARK, +}; + +static const unsigned int qspi_ctrl_b_pins[] = { + /* SPCLK, SSL */ + RCAR_GP_PIN(6, 0), RCAR_GP_PIN(6, 5), +}; +static const unsigned int qspi_ctrl_b_mux[] = { + SPCLK_B_MARK, SSL_B_MARK, +}; +static const unsigned int qspi_data2_b_pins[] = { + /* MOSI_IO0, MISO_IO1 */ + RCAR_GP_PIN(6, 1), RCAR_GP_PIN(6, 2), +}; +static const unsigned int qspi_data2_b_mux[] = { + MOSI_IO0_B_MARK, MISO_IO1_B_MARK, +}; +static const unsigned int qspi_data4_b_pins[] = { + /* MOSI_IO0, MISO_IO1, IO2, IO3 */ + RCAR_GP_PIN(6, 1), RCAR_GP_PIN(6, 2), RCAR_GP_PIN(6, 3), + RCAR_GP_PIN(6, 4), +}; +static const unsigned int qspi_data4_b_mux[] = { + SPCLK_B_MARK, MOSI_IO0_B_MARK, MISO_IO1_B_MARK, + IO2_B_MARK, IO3_B_MARK, SSL_B_MARK, +}; /* - SCIF0 ------------------------------------------------------------------ */ static const unsigned int scif0_data_pins[] = { /* RX, TX */ @@ -3123,6 +3583,12 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(i2c4), SH_PFC_PIN_GROUP(i2c4_b), SH_PFC_PIN_GROUP(i2c4_c), + SH_PFC_PIN_GROUP(i2c7), + SH_PFC_PIN_GROUP(i2c7_b), + SH_PFC_PIN_GROUP(i2c7_c), + SH_PFC_PIN_GROUP(i2c8), + SH_PFC_PIN_GROUP(i2c8_b), + SH_PFC_PIN_GROUP(i2c8_c), SH_PFC_PIN_GROUP(intc_irq0), SH_PFC_PIN_GROUP(intc_irq1), SH_PFC_PIN_GROUP(intc_irq2), @@ -3137,18 +3603,75 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(msiof0_ss2), SH_PFC_PIN_GROUP(msiof0_rx), SH_PFC_PIN_GROUP(msiof0_tx), + SH_PFC_PIN_GROUP(msiof0_clk_b), + SH_PFC_PIN_GROUP(msiof0_sync_b), + SH_PFC_PIN_GROUP(msiof0_ss1_b), + SH_PFC_PIN_GROUP(msiof0_ss2_b), + SH_PFC_PIN_GROUP(msiof0_rx_b), + SH_PFC_PIN_GROUP(msiof0_tx_b), + SH_PFC_PIN_GROUP(msiof0_clk_c), + SH_PFC_PIN_GROUP(msiof0_sync_c), + SH_PFC_PIN_GROUP(msiof0_ss1_c), + SH_PFC_PIN_GROUP(msiof0_ss2_c), + SH_PFC_PIN_GROUP(msiof0_rx_c), + SH_PFC_PIN_GROUP(msiof0_tx_c), SH_PFC_PIN_GROUP(msiof1_clk), SH_PFC_PIN_GROUP(msiof1_sync), SH_PFC_PIN_GROUP(msiof1_ss1), SH_PFC_PIN_GROUP(msiof1_ss2), SH_PFC_PIN_GROUP(msiof1_rx), SH_PFC_PIN_GROUP(msiof1_tx), + SH_PFC_PIN_GROUP(msiof1_clk_b), + SH_PFC_PIN_GROUP(msiof1_sync_b), + SH_PFC_PIN_GROUP(msiof1_ss1_b), + SH_PFC_PIN_GROUP(msiof1_ss2_b), + SH_PFC_PIN_GROUP(msiof1_rx_b), + SH_PFC_PIN_GROUP(msiof1_tx_b), + SH_PFC_PIN_GROUP(msiof1_clk_c), + SH_PFC_PIN_GROUP(msiof1_sync_c), + SH_PFC_PIN_GROUP(msiof1_rx_c), + SH_PFC_PIN_GROUP(msiof1_tx_c), + SH_PFC_PIN_GROUP(msiof1_clk_d), + SH_PFC_PIN_GROUP(msiof1_sync_d), + SH_PFC_PIN_GROUP(msiof1_ss1_d), + SH_PFC_PIN_GROUP(msiof1_rx_d), + SH_PFC_PIN_GROUP(msiof1_tx_d), + SH_PFC_PIN_GROUP(msiof1_clk_e), + SH_PFC_PIN_GROUP(msiof1_sync_e), + SH_PFC_PIN_GROUP(msiof1_rx_e), + SH_PFC_PIN_GROUP(msiof1_tx_e), SH_PFC_PIN_GROUP(msiof2_clk), SH_PFC_PIN_GROUP(msiof2_sync), SH_PFC_PIN_GROUP(msiof2_ss1), SH_PFC_PIN_GROUP(msiof2_ss2), SH_PFC_PIN_GROUP(msiof2_rx), SH_PFC_PIN_GROUP(msiof2_tx), + SH_PFC_PIN_GROUP(msiof2_clk_b), + SH_PFC_PIN_GROUP(msiof2_sync_b), + SH_PFC_PIN_GROUP(msiof2_ss1_b), + SH_PFC_PIN_GROUP(msiof2_ss2_b), + SH_PFC_PIN_GROUP(msiof2_rx_b), + SH_PFC_PIN_GROUP(msiof2_tx_b), + SH_PFC_PIN_GROUP(msiof2_clk_c), + SH_PFC_PIN_GROUP(msiof2_sync_c), + SH_PFC_PIN_GROUP(msiof2_rx_c), + SH_PFC_PIN_GROUP(msiof2_tx_c), + SH_PFC_PIN_GROUP(msiof2_clk_d), + SH_PFC_PIN_GROUP(msiof2_sync_d), + SH_PFC_PIN_GROUP(msiof2_ss1_d), + SH_PFC_PIN_GROUP(msiof2_ss2_d), + SH_PFC_PIN_GROUP(msiof2_rx_d), + SH_PFC_PIN_GROUP(msiof2_tx_d), + SH_PFC_PIN_GROUP(msiof2_clk_e), + SH_PFC_PIN_GROUP(msiof2_sync_e), + SH_PFC_PIN_GROUP(msiof2_rx_e), + SH_PFC_PIN_GROUP(msiof2_tx_e), + SH_PFC_PIN_GROUP(qspi_ctrl), + SH_PFC_PIN_GROUP(qspi_data2), + SH_PFC_PIN_GROUP(qspi_data4), + SH_PFC_PIN_GROUP(qspi_ctrl_b), + SH_PFC_PIN_GROUP(qspi_data2_b), + SH_PFC_PIN_GROUP(qspi_data4_b), SH_PFC_PIN_GROUP(scif0_data), SH_PFC_PIN_GROUP(scif0_data_b), SH_PFC_PIN_GROUP(scif0_data_c), @@ -3335,6 +3858,18 @@ static const char * const i2c4_groups[] = { "i2c4_c", }; +static const char * const i2c7_groups[] = { + "i2c7", + "i2c7_b", + "i2c7_c", +}; + +static const char * const i2c8_groups[] = { + "i2c8", + "i2c8_b", + "i2c8_c", +}; + static const char * const intc_groups[] = { "intc_irq0", "intc_irq1", @@ -3356,6 +3891,18 @@ static const char * const msiof0_groups[] = { "msiof0_ss2", "msiof0_rx", "msiof0_tx", + "msiof0_clk_b", + "msiof0_sync_b", + "msiof0_ss1_b", + "msiof0_ss2_b", + "msiof0_rx_b", + "msiof0_tx_b", + "msiof0_clk_c", + "msiof0_sync_c", + "msiof0_ss1_c", + "msiof0_ss2_c", + "msiof0_rx_c", + "msiof0_tx_c", }; static const char * const msiof1_groups[] = { @@ -3365,6 +3912,25 @@ static const char * const msiof1_groups[] = { "msiof1_ss2", "msiof1_rx", "msiof1_tx", + "msiof1_clk_b", + "msiof1_sync_b", + "msiof1_ss1_b", + "msiof1_ss2_b", + "msiof1_rx_b", + "msiof1_tx_b", + "msiof1_clk_c", + "msiof1_sync_c", + "msiof1_rx_c", + "msiof1_tx_c", + "msiof1_clk_d", + "msiof1_sync_d", + "msiof1_ss1_d", + "msiof1_rx_d", + "msiof1_tx_d", + "msiof1_clk_e", + "msiof1_sync_e", + "msiof1_rx_e", + "msiof1_tx_e", }; static const char * const msiof2_groups[] = { @@ -3374,6 +3940,35 @@ static const char * const msiof2_groups[] = { "msiof2_ss2", "msiof2_rx", "msiof2_tx", + "msiof2_clk_b", + "msiof2_sync_b", + "msiof2_ss1_b", + "msiof2_ss2_b", + "msiof2_rx_b", + "msiof2_tx_b", + "msiof2_clk_c", + "msiof2_sync_c", + "msiof2_rx_c", + "msiof2_tx_c", + "msiof2_clk_d", + "msiof2_sync_d", + "msiof2_ss1_d", + "msiof2_ss2_d", + "msiof2_rx_d", + "msiof2_tx_d", + "msiof2_clk_e", + "msiof2_sync_e", + "msiof2_rx_e", + "msiof2_tx_e", +}; + +static const char * const qspi_groups[] = { + "qspi_ctrl", + "qspi_data2", + "qspi_data4", + "qspi_ctrl_b", + "qspi_data2_b", + "qspi_data4_b", }; static const char * const scif0_groups[] = { @@ -3566,11 +4161,14 @@ static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(i2c2), SH_PFC_FUNCTION(i2c3), SH_PFC_FUNCTION(i2c4), + SH_PFC_FUNCTION(i2c7), + SH_PFC_FUNCTION(i2c8), SH_PFC_FUNCTION(intc), SH_PFC_FUNCTION(mmc), SH_PFC_FUNCTION(msiof0), SH_PFC_FUNCTION(msiof1), SH_PFC_FUNCTION(msiof2), + SH_PFC_FUNCTION(qspi), SH_PFC_FUNCTION(scif0), SH_PFC_FUNCTION(scif1), SH_PFC_FUNCTION(scif2), @@ -3825,7 +4423,7 @@ static const struct pinmux_cfg_reg pinmux_config_regs[] = { GP_6_11_FN, FN_IP13_25, GP_6_10_FN, FN_IP13_24_23, GP_6_9_FN, FN_IP13_22, - 0, 0, + GP_6_8_FN, FN_SD1_CLK, GP_6_7_FN, FN_IP13_21_19, GP_6_6_FN, FN_IP13_18_16, GP_6_5_FN, FN_IP13_15, diff --git a/drivers/pinctrl/sirf/pinctrl-atlas6.c b/drivers/pinctrl/sirf/pinctrl-atlas6.c index 2b9f32065920..c4dd3d5cf9c3 100644 --- a/drivers/pinctrl/sirf/pinctrl-atlas6.c +++ b/drivers/pinctrl/sirf/pinctrl-atlas6.c @@ -1,7 +1,8 @@ /* * pinctrl pads, groups, functions for CSR SiRFatlasVI * - * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group + * company. * * Licensed under GPLv2 or later. */ @@ -529,6 +530,40 @@ static const struct sirfsoc_padmux usp0_padmux = { static const unsigned usp0_pins[] = { 51, 52, 53, 54, 55 }; +static const struct sirfsoc_muxmask usp0_only_utfs_muxmask[] = { + { + .group = 1, + .mask = BIT(19) | BIT(20) | BIT(21) | BIT(22), + }, +}; + +static const struct sirfsoc_padmux usp0_only_utfs_padmux = { + .muxmask_counts = ARRAY_SIZE(usp0_only_utfs_muxmask), + .muxmask = usp0_only_utfs_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, + .funcmask = BIT(1) | BIT(2) | BIT(6), + .funcval = 0, +}; + +static const unsigned usp0_only_utfs_pins[] = { 51, 52, 53, 54 }; + +static const struct sirfsoc_muxmask usp0_only_urfs_muxmask[] = { + { + .group = 1, + .mask = BIT(19) | BIT(20) | BIT(21) | BIT(23), + }, +}; + +static const struct sirfsoc_padmux usp0_only_urfs_padmux = { + .muxmask_counts = ARRAY_SIZE(usp0_only_urfs_muxmask), + .muxmask = usp0_only_urfs_muxmask, + .ctrlreg = SIRFSOC_RSC_PIN_MUX, + .funcmask = BIT(1) | BIT(2) | BIT(9), + .funcval = 0, +}; + +static const unsigned usp0_only_urfs_pins[] = { 51, 52, 53, 55 }; + static const struct sirfsoc_muxmask usp0_uart_nostreamctrl_muxmask[] = { { .group = 1, @@ -905,6 +940,8 @@ static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = { SIRFSOC_PIN_GROUP("usp0grp", usp0_pins), SIRFSOC_PIN_GROUP("usp0_uart_nostreamctrl_grp", usp0_uart_nostreamctrl_pins), + SIRFSOC_PIN_GROUP("usp0_only_utfs_grp", usp0_only_utfs_pins), + SIRFSOC_PIN_GROUP("usp0_only_urfs_grp", usp0_only_urfs_pins), SIRFSOC_PIN_GROUP("usp1grp", usp1_pins), SIRFSOC_PIN_GROUP("usp1_uart_nostreamctrl_grp", usp1_uart_nostreamctrl_pins), @@ -953,6 +990,9 @@ static const char * const uart2_nostreamctrlgrp[] = { "uart2_nostreamctrlgrp" }; static const char * const usp0_uart_nostreamctrl_grp[] = { "usp0_uart_nostreamctrl_grp" }; static const char * const usp0grp[] = { "usp0grp" }; +static const char * const usp0_only_utfs_grp[] = { "usp0_only_utfs_grp" }; +static const char * const usp0_only_urfs_grp[] = { "usp0_only_urfs_grp" }; + static const char * const usp1grp[] = { "usp1grp" }; static const char * const usp1_uart_nostreamctrl_grp[] = { "usp1_uart_nostreamctrl_grp" }; @@ -1003,6 +1043,10 @@ static const struct sirfsoc_pmx_func sirfsoc_pmx_functions[] = { SIRFSOC_PMX_FUNCTION("usp0_uart_nostreamctrl", usp0_uart_nostreamctrl_grp, usp0_uart_nostreamctrl_padmux), + SIRFSOC_PMX_FUNCTION("usp0_only_utfs", usp0_only_utfs_grp, + usp0_only_utfs_padmux), + SIRFSOC_PMX_FUNCTION("usp0_only_urfs", usp0_only_urfs_grp, + usp0_only_urfs_padmux), SIRFSOC_PMX_FUNCTION("usp1", usp1grp, usp1_padmux), SIRFSOC_PMX_FUNCTION("usp1_uart_nostreamctrl", usp1_uart_nostreamctrl_grp, diff --git a/drivers/pinctrl/sirf/pinctrl-prima2.c b/drivers/pinctrl/sirf/pinctrl-prima2.c index dde0285544d6..8aa76f0776d7 100644 --- a/drivers/pinctrl/sirf/pinctrl-prima2.c +++ b/drivers/pinctrl/sirf/pinctrl-prima2.c @@ -1,7 +1,8 @@ /* * pinctrl pads, groups, functions for CSR SiRFprimaII * - * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group + * company. * * Licensed under GPLv2 or later. */ diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c index a0d6152701cd..5f3adb87c1ef 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -1,7 +1,8 @@ /* * pinmux driver for CSR SiRFprimaII * - * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * Copyright (c) 2011 - 2014 Cambridge Silicon Radio Limited, a CSR plc group + * company. * * Licensed under GPLv2 or later. */ @@ -598,7 +599,7 @@ static unsigned int sirfsoc_gpio_irq_startup(struct irq_data *d) { struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); - if (gpio_lock_as_irq(&bank->chip.gc, d->hwirq)) + if (gpio_lock_as_irq(&bank->chip.gc, d->hwirq % SIRFSOC_GPIO_BANK_SIZE)) dev_err(bank->chip.gc.dev, "unable to lock HW IRQ %lu for IRQ\n", d->hwirq); @@ -611,7 +612,7 @@ static void sirfsoc_gpio_irq_shutdown(struct irq_data *d) struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); sirfsoc_gpio_irq_mask(d); - gpio_unlock_as_irq(&bank->chip.gc, d->hwirq); + gpio_unlock_as_irq(&bank->chip.gc, d->hwirq % SIRFSOC_GPIO_BANK_SIZE); } static struct irq_chip sirfsoc_irq_chip = { diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 5ae65c11d544..5f67843c7fb7 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -27,8 +27,6 @@ config ACER_WMI depends on ACPI_WMI select INPUT_SPARSEKMAP # Acer WMI depends on ACPI_VIDEO when ACPI is enabled - # but for select to work, need to select ACPI_VIDEO's dependencies, ick - select VIDEO_OUTPUT_CONTROL if ACPI select ACPI_VIDEO if ACPI ---help--- This is a driver for newer Acer (and Wistron) laptops. It adds diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index be02bcc346d3..e6f336270c21 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -66,7 +66,6 @@ #include <linux/backlight.h> #include <linux/input.h> #include <linux/kfifo.h> -#include <linux/video_output.h> #include <linux/platform_device.h> #include <linux/slab.h> #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index defb6afc1409..94bb6157c957 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6776,8 +6776,9 @@ static int __init volume_create_alsa_mixer(void) struct snd_kcontrol *ctl_mute; int rc; - rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, - sizeof(struct tpacpi_alsa_data), &card); + rc = snd_card_new(&tpacpi_pdev->dev, + alsa_index, alsa_id, THIS_MODULE, + sizeof(struct tpacpi_alsa_data), &card); if (rc < 0 || !card) { pr_err("Failed to create ALSA card structures: %d\n", rc); return 1; @@ -6828,7 +6829,6 @@ static int __init volume_create_alsa_mixer(void) } data->ctl_mute_id = &ctl_mute->id; - snd_card_set_dev(card, &tpacpi_pdev->dev); rc = snd_card_register(card); if (rc < 0) { pr_err("Failed to register ALSA card: %d\n", rc); diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 167f3d00c916..66977ebf13b3 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -183,9 +183,7 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, struct resource r = {0}; int i, flags; - if (acpi_dev_resource_memory(res, &r) - || acpi_dev_resource_io(res, &r) - || acpi_dev_resource_address_space(res, &r) + if (acpi_dev_resource_address_space(res, &r) || acpi_dev_resource_ext_address_space(res, &r)) { pnp_add_resource(dev, &r); return AE_OK; @@ -217,6 +215,17 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, } switch (res->type) { + 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); + break; + case ACPI_RESOURCE_TYPE_IO: + case ACPI_RESOURCE_TYPE_FIXED_IO: + if (acpi_dev_resource_io(res, &r)) + pnp_add_resource(dev, &r); + break; case ACPI_RESOURCE_TYPE_DMA: dma = &res->data.dma; if (dma->channel_count > 0 && dma->channels[0] != (u8) -1) diff --git a/drivers/pnp/pnpbios/bioscalls.c b/drivers/pnp/pnpbios/bioscalls.c index 769d265b221b..deb7f4bcdb7b 100644 --- a/drivers/pnp/pnpbios/bioscalls.c +++ b/drivers/pnp/pnpbios/bioscalls.c @@ -21,7 +21,7 @@ #include "pnpbios.h" -static struct { +__visible struct { u16 offset; u16 segment; } pnp_bios_callpoint; @@ -41,6 +41,7 @@ asmlinkage void pnp_bios_callfunc(void); __asm__(".text \n" __ALIGN_STR "\n" + ".globl pnp_bios_callfunc\n" "pnp_bios_callfunc:\n" " pushl %edx \n" " pushl %ecx \n" @@ -66,9 +67,9 @@ static struct desc_struct bad_bios_desc = GDT_ENTRY_INIT(0x4092, * after PnP BIOS oopses. */ -u32 pnp_bios_fault_esp; -u32 pnp_bios_fault_eip; -u32 pnp_bios_is_utter_crap = 0; +__visible u32 pnp_bios_fault_esp; +__visible u32 pnp_bios_fault_eip; +__visible u32 pnp_bios_is_utter_crap = 0; static spinlock_t pnp_bios_lock; diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index 3c6768378a94..61b51e17d932 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -834,7 +834,7 @@ static int rapl_write_data_raw(struct rapl_domain *rd, } static const struct x86_cpu_id energy_unit_quirk_ids[] = { - { X86_VENDOR_INTEL, 6, 0x37},/* VLV */ + { X86_VENDOR_INTEL, 6, 0x37},/* Valleyview */ {} }; @@ -947,11 +947,11 @@ static void package_power_limit_irq_restore(int package_id) } static const struct x86_cpu_id rapl_ids[] = { - { X86_VENDOR_INTEL, 6, 0x2a},/* SNB */ - { X86_VENDOR_INTEL, 6, 0x2d},/* SNB EP */ - { X86_VENDOR_INTEL, 6, 0x37},/* VLV */ - { X86_VENDOR_INTEL, 6, 0x3a},/* IVB */ - { X86_VENDOR_INTEL, 6, 0x45},/* HSW */ + { X86_VENDOR_INTEL, 6, 0x2a},/* Sandy Bridge */ + { X86_VENDOR_INTEL, 6, 0x2d},/* Sandy Bridge EP */ + { X86_VENDOR_INTEL, 6, 0x37},/* Valleyview */ + { X86_VENDOR_INTEL, 6, 0x3a},/* Ivy Bridge */ + { X86_VENDOR_INTEL, 6, 0x45},/* Haswell */ /* TODO: Add more CPU IDs after testing */ {} }; @@ -1147,6 +1147,11 @@ static int rapl_check_domain(int cpu, int domain) if (rdmsrl_safe_on_cpu(cpu, msr, &val1)) return -ENODEV; + /* PP1/uncore/graphics domain may not be active at the time of + * driver loading. So skip further checks. + */ + if (domain == RAPL_DOMAIN_PP1) + return 0; /* energy counters roll slowly on some domains */ while (++retry < 10) { usleep_range(10000, 15000); diff --git a/drivers/ps3/ps3-vuart.c b/drivers/ps3/ps3-vuart.c index fb7300837fee..bc1e5139ba29 100644 --- a/drivers/ps3/ps3-vuart.c +++ b/drivers/ps3/ps3-vuart.c @@ -699,8 +699,6 @@ int ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes) BUG_ON(!bytes); - PREPARE_WORK(&priv->rx_list.work.work, ps3_vuart_work); - spin_lock_irqsave(&priv->rx_list.lock, flags); if (priv->rx_list.bytes_held >= bytes) { dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n", @@ -1052,7 +1050,7 @@ static int ps3_vuart_probe(struct ps3_system_bus_device *dev) INIT_LIST_HEAD(&priv->rx_list.head); spin_lock_init(&priv->rx_list.lock); - INIT_WORK(&priv->rx_list.work.work, NULL); + INIT_WORK(&priv->rx_list.work.work, ps3_vuart_work); priv->rx_list.work.trigger = 0; priv->rx_list.work.dev = dev; diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h index b4b0d83f9ef6..7061ac0ad428 100644 --- a/drivers/rapidio/devices/tsi721.h +++ b/drivers/rapidio/devices/tsi721.h @@ -678,6 +678,7 @@ struct tsi721_bdma_chan { struct list_head free_list; dma_cookie_t completed_cookie; struct tasklet_struct tasklet; + bool active; }; #endif /* CONFIG_RAPIDIO_DMA_ENGINE */ diff --git a/drivers/rapidio/devices/tsi721_dma.c b/drivers/rapidio/devices/tsi721_dma.c index 502663f5f7c6..91245f5dbe81 100644 --- a/drivers/rapidio/devices/tsi721_dma.c +++ b/drivers/rapidio/devices/tsi721_dma.c @@ -206,8 +206,8 @@ void tsi721_bdma_handler(struct tsi721_bdma_chan *bdma_chan) { /* Disable BDMA channel interrupts */ iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE); - - tasklet_schedule(&bdma_chan->tasklet); + if (bdma_chan->active) + tasklet_schedule(&bdma_chan->tasklet); } #ifdef CONFIG_PCI_MSI @@ -562,7 +562,7 @@ static int tsi721_alloc_chan_resources(struct dma_chan *dchan) } #endif /* CONFIG_PCI_MSI */ - tasklet_enable(&bdma_chan->tasklet); + bdma_chan->active = true; tsi721_bdma_interrupt_enable(bdma_chan, 1); return bdma_chan->bd_num - 1; @@ -576,9 +576,7 @@ err_out: static void tsi721_free_chan_resources(struct dma_chan *dchan) { struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan); -#ifdef CONFIG_PCI_MSI struct tsi721_device *priv = to_tsi721(dchan->device); -#endif LIST_HEAD(list); dev_dbg(dchan->device->dev, "%s: Entry\n", __func__); @@ -589,14 +587,25 @@ static void tsi721_free_chan_resources(struct dma_chan *dchan) BUG_ON(!list_empty(&bdma_chan->active_list)); BUG_ON(!list_empty(&bdma_chan->queue)); - tasklet_disable(&bdma_chan->tasklet); + tsi721_bdma_interrupt_enable(bdma_chan, 0); + bdma_chan->active = false; + +#ifdef CONFIG_PCI_MSI + if (priv->flags & TSI721_USING_MSIX) { + synchronize_irq(priv->msix[TSI721_VECT_DMA0_DONE + + bdma_chan->id].vector); + synchronize_irq(priv->msix[TSI721_VECT_DMA0_INT + + bdma_chan->id].vector); + } else +#endif + synchronize_irq(priv->pdev->irq); + + tasklet_kill(&bdma_chan->tasklet); spin_lock_bh(&bdma_chan->lock); list_splice_init(&bdma_chan->free_list, &list); spin_unlock_bh(&bdma_chan->lock); - tsi721_bdma_interrupt_enable(bdma_chan, 0); - #ifdef CONFIG_PCI_MSI if (priv->flags & TSI721_USING_MSIX) { free_irq(priv->msix[TSI721_VECT_DMA0_DONE + @@ -790,6 +799,7 @@ int tsi721_register_dma(struct tsi721_device *priv) bdma_chan->dchan.cookie = 1; bdma_chan->dchan.chan_id = i; bdma_chan->id = i; + bdma_chan->active = false; spin_lock_init(&bdma_chan->lock); @@ -799,7 +809,6 @@ int tsi721_register_dma(struct tsi721_device *priv) tasklet_init(&bdma_chan->tasklet, tsi721_dma_tasklet, (unsigned long)bdma_chan); - tasklet_disable(&bdma_chan->tasklet); list_add_tail(&bdma_chan->dchan.device_node, &mport->dma.channels); } diff --git a/drivers/regulator/88pm800.c b/drivers/regulator/88pm800.c index d333f7eac106..7a721d67e6ac 100644 --- a/drivers/regulator/88pm800.c +++ b/drivers/regulator/88pm800.c @@ -310,10 +310,8 @@ static int pm800_regulator_probe(struct platform_device *pdev) pm800_data = devm_kzalloc(&pdev->dev, sizeof(*pm800_data), GFP_KERNEL); - if (!pm800_data) { - dev_err(&pdev->dev, "Failed to allocate pm800_regualtors"); + if (!pm800_data) return -ENOMEM; - } pm800_data->map = chip->subchip->regmap_power; pm800_data->chip = chip; diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index f704d83c93c4..337634ad0562 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -2,7 +2,7 @@ * Regulators driver for Marvell 88PM8607 * * Copyright (C) 2009 Marvell International Ltd. - * Haojian Zhuang <haojian.zhuang@marvell.com> + * Haojian Zhuang <haojian.zhuang@marvell.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 @@ -78,7 +78,7 @@ static const unsigned int BUCK2_suspend_table[] = { }; static const unsigned int BUCK3_table[] = { - 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, + 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, 200000, 225000, 250000, 275000, 300000, 325000, 350000, 375000, 400000, 425000, 450000, 475000, 500000, 525000, 550000, 575000, 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, @@ -89,7 +89,7 @@ static const unsigned int BUCK3_table[] = { }; static const unsigned int BUCK3_suspend_table[] = { - 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, + 0, 25000, 50000, 75000, 100000, 125000, 150000, 175000, 200000, 225000, 250000, 275000, 300000, 325000, 350000, 375000, 400000, 425000, 450000, 475000, 500000, 525000, 550000, 575000, 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, @@ -322,7 +322,7 @@ static int pm8607_regulator_dt_init(struct platform_device *pdev, nproot = of_node_get(pdev->dev.parent->of_node); if (!nproot) return -ENODEV; - nproot = of_find_node_by_name(nproot, "regulators"); + nproot = of_get_child_by_name(nproot, "regulators"); if (!nproot) { dev_err(&pdev->dev, "failed to find regulators node\n"); return -ENODEV; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6a7932822e37..1cd8584a7b88 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -139,6 +139,14 @@ config REGULATOR_AS3722 AS3722 PMIC. This will enable support for all the software controllable DCDC/LDO regulators. +config REGULATOR_BCM590XX + tristate "Broadcom BCM590xx PMU Regulators" + depends on MFD_BCM590XX + help + This driver provides support for the voltage regulators on the + BCM590xx PMUs. This will enable support for the software + controllable LDO/Switching regulators. + config REGULATOR_DA903X tristate "Dialog Semiconductor DA9030/DA9034 regulators" depends on PMIC_DA903X @@ -399,12 +407,12 @@ config REGULATOR_PCF50633 on PCF50633 config REGULATOR_PFUZE100 - tristate "Freescale PFUZE100 regulator driver" + tristate "Freescale PFUZE100/PFUZE200 regulator driver" depends on I2C select REGMAP_I2C help - Say y here to support the regulators found on the Freescale PFUZE100 - PMIC. + Say y here to support the regulators found on the Freescale + PFUZE100/PFUZE200 PMIC. config REGULATOR_RC5T583 tristate "RICOH RC5T583 Power regulators" @@ -416,13 +424,21 @@ config REGULATOR_RC5T583 through regulator interface. The device supports multiple DCDC/LDO outputs which can be controlled by i2c communication. +config REGULATOR_S2MPA01 + tristate "Samsung S2MPA01 voltage regulator" + depends on MFD_SEC_CORE + help + This driver controls Samsung S2MPA01 voltage output regulator + via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs. + config REGULATOR_S2MPS11 - tristate "Samsung S2MPS11 voltage regulator" + tristate "Samsung S2MPS11/S2MPS14 voltage regulator" depends on MFD_SEC_CORE help - This driver supports a Samsung S2MPS11 voltage output regulator - via I2C bus. S2MPS11 is comprised of high efficient Buck converters - including Dual-Phase Buck converter, Buck-Boost converter, various LDOs. + This driver supports a Samsung S2MPS11/S2MPS14 voltage output + regulator via I2C bus. The chip is comprised of high efficient Buck + converters including Dual-Phase Buck converter, Buck-Boost converter, + various LDOs. config REGULATOR_S5M8767 tristate "Samsung S5M8767A voltage regulator" @@ -432,6 +448,12 @@ config REGULATOR_S5M8767 via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and supports DVS mode with 8bits of output voltage control. +config REGULATOR_ST_PWM + tristate "STMicroelectronics PWM voltage regulator" + depends on ARCH_STI + help + This driver supports ST's PWM controlled voltage regulators. + config REGULATOR_TI_ABB tristate "TI Adaptive Body Bias on-chip LDO" depends on ARCH_OMAP @@ -513,6 +535,15 @@ config REGULATOR_TPS65217 voltage regulators. It supports software based voltage control for different voltage domains +config REGULATOR_TPS65218 + tristate "TI TPS65218 Power regulators" + depends on MFD_TPS65218 && OF + help + This driver supports TPS65218 voltage regulator chips. TPS65218 + provides six step-down converters and one general-purpose LDO + voltage regulators. It supports software based voltage control + for different voltage domains + config REGULATOR_TPS6524X tristate "TI TPS6524X Power regulators" depends on SPI diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 979f9ddcf259..f0fe0c50b59c 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o +obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o @@ -57,8 +58,10 @@ obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o +obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o +obj-$(CONFIG_REGULATOR_ST_PWM) += st-pwm.o obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o @@ -67,6 +70,7 @@ obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o obj-$(CONFIG_REGULATOR_TPS65090) += tps65090-regulator.o obj-$(CONFIG_REGULATOR_TPS65217) += tps65217-regulator.o +obj-$(CONFIG_REGULATOR_TPS65218) += tps65218-regulator.o obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c index f70a9bfa5ff2..c873ee0082cf 100644 --- a/drivers/regulator/aat2870-regulator.c +++ b/drivers/regulator/aat2870-regulator.c @@ -99,6 +99,7 @@ static int aat2870_ldo_is_enabled(struct regulator_dev *rdev) static struct regulator_ops aat2870_ldo_ops = { .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, .set_voltage_sel = aat2870_ldo_set_voltage_sel, .get_voltage_sel = aat2870_ldo_get_voltage_sel, .enable = aat2870_ldo_enable, diff --git a/drivers/regulator/act8865-regulator.c b/drivers/regulator/act8865-regulator.c index 084cc0819a52..b92d7dd01a18 100644 --- a/drivers/regulator/act8865-regulator.c +++ b/drivers/regulator/act8865-regulator.c @@ -62,7 +62,6 @@ #define ACT8865_VOLTAGE_NUM 64 struct act8865 { - struct regulator_dev *rdev[ACT8865_REG_NUM]; struct regmap *regmap; }; @@ -213,7 +212,7 @@ static int act8865_pdata_from_dt(struct device *dev, struct device_node *np; struct act8865_regulator_data *regulator; - np = of_find_node_by_name(dev->of_node, "regulators"); + np = of_get_child_by_name(dev->of_node, "regulators"); if (!np) { dev_err(dev, "missing 'regulators' subnode in DT\n"); return -EINVAL; @@ -221,17 +220,15 @@ static int act8865_pdata_from_dt(struct device *dev, matched = of_regulator_match(dev, np, act8865_matches, ARRAY_SIZE(act8865_matches)); + of_node_put(np); if (matched <= 0) return matched; pdata->regulators = devm_kzalloc(dev, sizeof(struct act8865_regulator_data) * ARRAY_SIZE(act8865_matches), GFP_KERNEL); - if (!pdata->regulators) { - dev_err(dev, "%s: failed to allocate act8865 registor\n", - __func__); + if (!pdata->regulators) return -ENOMEM; - } pdata->num_regulators = matched; regulator = pdata->regulators; @@ -258,7 +255,7 @@ static inline int act8865_pdata_from_dt(struct device *dev, static int act8865_pmic_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { - struct regulator_dev **rdev; + struct regulator_dev *rdev; struct device *dev = &client->dev; struct act8865_platform_data *pdata = dev_get_platdata(dev); struct regulator_config config = { }; @@ -292,8 +289,6 @@ static int act8865_pmic_probe(struct i2c_client *client, if (!act8865) return -ENOMEM; - rdev = act8865->rdev; - act8865->regmap = devm_regmap_init_i2c(client, &act8865_regmap_config); if (IS_ERR(act8865->regmap)) { error = PTR_ERR(act8865->regmap); @@ -313,12 +308,12 @@ static int act8865_pmic_probe(struct i2c_client *client, config.driver_data = act8865; config.regmap = act8865->regmap; - rdev[i] = devm_regulator_register(&client->dev, - &act8865_reg[i], &config); - if (IS_ERR(rdev[i])) { + rdev = devm_regulator_register(&client->dev, &act8865_reg[i], + &config); + if (IS_ERR(rdev)) { dev_err(dev, "failed to register %s\n", act8865_reg[id].name); - return PTR_ERR(rdev[i]); + return PTR_ERR(rdev); } } diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 862e63e451d0..7c397bb81e01 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -34,6 +34,9 @@ #define LDO_RAMP_UP_UNIT_IN_CYCLES 64 /* 64 cycles per step */ #define LDO_RAMP_UP_FREQ_IN_MHZ 24 /* cycle based on 24M OSC */ +#define LDO_POWER_GATE 0x00 +#define LDO_FET_FULL_ON 0x1f + struct anatop_regulator { const char *name; u32 control_reg; @@ -48,19 +51,10 @@ struct anatop_regulator { int max_voltage; struct regulator_desc rdesc; struct regulator_init_data *initdata; + bool bypass; + int sel; }; -static int anatop_regmap_set_voltage_sel(struct regulator_dev *reg, - unsigned selector) -{ - struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); - - if (!anatop_reg->control_reg) - return -ENOTSUPP; - - return regulator_set_voltage_sel_regmap(reg, selector); -} - static int anatop_regmap_set_voltage_time_sel(struct regulator_dev *reg, unsigned int old_sel, unsigned int new_sel) @@ -87,22 +81,99 @@ static int anatop_regmap_set_voltage_time_sel(struct regulator_dev *reg, return ret; } -static int anatop_regmap_get_voltage_sel(struct regulator_dev *reg) +static int anatop_regmap_enable(struct regulator_dev *reg) { struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + int sel; - if (!anatop_reg->control_reg) - return -ENOTSUPP; + sel = anatop_reg->bypass ? LDO_FET_FULL_ON : anatop_reg->sel; + return regulator_set_voltage_sel_regmap(reg, sel); +} + +static int anatop_regmap_disable(struct regulator_dev *reg) +{ + return regulator_set_voltage_sel_regmap(reg, LDO_POWER_GATE); +} + +static int anatop_regmap_is_enabled(struct regulator_dev *reg) +{ + return regulator_get_voltage_sel_regmap(reg) != LDO_POWER_GATE; +} + +static int anatop_regmap_core_set_voltage_sel(struct regulator_dev *reg, + unsigned selector) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + int ret; + + if (anatop_reg->bypass || !anatop_regmap_is_enabled(reg)) { + anatop_reg->sel = selector; + return 0; + } + + ret = regulator_set_voltage_sel_regmap(reg, selector); + if (!ret) + anatop_reg->sel = selector; + return ret; +} + +static int anatop_regmap_core_get_voltage_sel(struct regulator_dev *reg) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + + if (anatop_reg->bypass || !anatop_regmap_is_enabled(reg)) + return anatop_reg->sel; return regulator_get_voltage_sel_regmap(reg); } +static int anatop_regmap_get_bypass(struct regulator_dev *reg, bool *enable) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + int sel; + + sel = regulator_get_voltage_sel_regmap(reg); + if (sel == LDO_FET_FULL_ON) + WARN_ON(!anatop_reg->bypass); + else if (sel != LDO_POWER_GATE) + WARN_ON(anatop_reg->bypass); + + *enable = anatop_reg->bypass; + return 0; +} + +static int anatop_regmap_set_bypass(struct regulator_dev *reg, bool enable) +{ + struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); + int sel; + + if (enable == anatop_reg->bypass) + return 0; + + sel = enable ? LDO_FET_FULL_ON : anatop_reg->sel; + anatop_reg->bypass = enable; + + return regulator_set_voltage_sel_regmap(reg, sel); +} + static struct regulator_ops anatop_rops = { - .set_voltage_sel = anatop_regmap_set_voltage_sel, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static struct regulator_ops anatop_core_rops = { + .enable = anatop_regmap_enable, + .disable = anatop_regmap_disable, + .is_enabled = anatop_regmap_is_enabled, + .set_voltage_sel = anatop_regmap_core_set_voltage_sel, .set_voltage_time_sel = anatop_regmap_set_voltage_time_sel, - .get_voltage_sel = anatop_regmap_get_voltage_sel, + .get_voltage_sel = anatop_regmap_core_get_voltage_sel, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, + .get_bypass = anatop_regmap_get_bypass, + .set_bypass = anatop_regmap_set_bypass, }; static int anatop_regulator_probe(struct platform_device *pdev) @@ -116,6 +187,7 @@ static int anatop_regulator_probe(struct platform_device *pdev) struct regulator_init_data *initdata; struct regulator_config config = { }; int ret = 0; + u32 val; initdata = of_get_regulator_init_data(dev, np); sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL); @@ -125,7 +197,6 @@ static int anatop_regulator_probe(struct platform_device *pdev) sreg->name = of_get_property(np, "regulator-name", NULL); rdesc = &sreg->rdesc; rdesc->name = sreg->name; - rdesc->ops = &anatop_rops; rdesc->type = REGULATOR_VOLTAGE; rdesc->owner = THIS_MODULE; @@ -197,6 +268,25 @@ static int anatop_regulator_probe(struct platform_device *pdev) config.of_node = pdev->dev.of_node; config.regmap = sreg->anatop; + /* Only core regulators have the ramp up delay configuration. */ + if (sreg->control_reg && sreg->delay_bit_width) { + rdesc->ops = &anatop_core_rops; + + ret = regmap_read(config.regmap, rdesc->vsel_reg, &val); + if (ret) { + dev_err(dev, "failed to read initial state\n"); + return ret; + } + + sreg->sel = (val & rdesc->vsel_mask) >> sreg->vol_bit_shift; + if (sreg->sel == LDO_FET_FULL_ON) { + sreg->sel = 0; + sreg->bypass = true; + } + } else { + rdesc->ops = &anatop_rops; + } + /* register regulator */ rdev = devm_regulator_register(dev, rdesc, &config); if (IS_ERR(rdev)) { diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 4f6c2055f6b2..b1033d30b504 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -153,11 +153,9 @@ static const struct regulator_desc arizona_ldo1 = { .vsel_reg = ARIZONA_LDO1_CONTROL_1, .vsel_mask = ARIZONA_LDO1_VSEL_MASK, - .bypass_reg = ARIZONA_LDO1_CONTROL_1, - .bypass_mask = ARIZONA_LDO1_BYPASS, .min_uV = 900000, - .uV_step = 50000, - .n_voltages = 7, + .uV_step = 25000, + .n_voltages = 13, .enable_time = 500, .owner = THIS_MODULE, @@ -189,10 +187,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev) int ret; ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL); - if (ldo1 == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!ldo1) return -ENOMEM; - } ldo1->arizona = arizona; @@ -203,6 +199,7 @@ static int arizona_ldo1_probe(struct platform_device *pdev) */ switch (arizona->type) { case WM5102: + case WM8997: desc = &arizona_ldo1_hc; ldo1->init_data = arizona_ldo1_dvfs; break; diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 034ece707083..6fdd9bf6927f 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -204,10 +204,8 @@ static int arizona_micsupp_probe(struct platform_device *pdev) int ret; micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL); - if (micsupp == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!micsupp) return -ENOMEM; - } micsupp->arizona = arizona; INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp); diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c index c77a58478cca..b47283f91e2d 100644 --- a/drivers/regulator/as3711-regulator.c +++ b/drivers/regulator/as3711-regulator.c @@ -191,7 +191,7 @@ static int as3711_regulator_parse_dt(struct device *dev, { struct as3711_regulator_pdata *pdata = dev_get_platdata(dev); struct device_node *regulators = - of_find_node_by_name(dev->parent->of_node, "regulators"); + of_get_child_by_name(dev->parent->of_node, "regulators"); struct of_regulator_match *match; int ret, i; @@ -221,7 +221,6 @@ static int as3711_regulator_probe(struct platform_device *pdev) { struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev); struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent); - struct regulator_init_data *reg_data; struct regulator_config config = {.dev = &pdev->dev,}; struct as3711_regulator *reg = NULL; struct as3711_regulator *regs; @@ -246,22 +245,14 @@ static int as3711_regulator_probe(struct platform_device *pdev) regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM * sizeof(struct as3711_regulator), GFP_KERNEL); - if (!regs) { - dev_err(&pdev->dev, "Memory allocation failed exiting..\n"); + if (!regs) return -ENOMEM; - } for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) { - reg_data = pdata->init_data[id]; - - /* No need to register if there is no regulator data */ - if (!reg_data) - continue; - reg = ®s[id]; reg->reg_info = ri; - config.init_data = reg_data; + config.init_data = pdata->init_data[id]; config.driver_data = reg; config.regmap = as3711->regmap; config.of_node = of_node[id]; diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index 8b17d786cb71..85585219ce82 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -719,6 +719,7 @@ static int as3722_get_regulator_dt_data(struct platform_device *pdev, ret = of_regulator_match(&pdev->dev, np, as3722_regulator_matches, ARRAY_SIZE(as3722_regulator_matches)); + of_node_put(np); if (ret < 0) { dev_err(&pdev->dev, "Parsing of regulator node failed: %d\n", ret); diff --git a/drivers/regulator/bcm590xx-regulator.c b/drivers/regulator/bcm590xx-regulator.c new file mode 100644 index 000000000000..ab08ca7cfb08 --- /dev/null +++ b/drivers/regulator/bcm590xx-regulator.c @@ -0,0 +1,403 @@ +/* + * Broadcom BCM590xx regulator driver + * + * Copyright 2014 Linaro Limited + * Author: Matt Porter <mporter@linaro.org> + * + * 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/err.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mfd/bcm590xx.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/slab.h> + +/* Register defs */ +#define BCM590XX_RFLDOPMCTRL1 0x60 +#define BCM590XX_IOSR1PMCTRL1 0x7a +#define BCM590XX_IOSR2PMCTRL1 0x7c +#define BCM590XX_CSRPMCTRL1 0x7e +#define BCM590XX_SDSR1PMCTRL1 0x82 +#define BCM590XX_SDSR2PMCTRL1 0x86 +#define BCM590XX_MSRPMCTRL1 0x8a +#define BCM590XX_VSRPMCTRL1 0x8e +#define BCM590XX_REG_ENABLE BIT(7) + +#define BCM590XX_RFLDOCTRL 0x96 +#define BCM590XX_CSRVOUT1 0xc0 +#define BCM590XX_LDO_VSEL_MASK GENMASK(5, 3) +#define BCM590XX_SR_VSEL_MASK GENMASK(5, 0) + +/* LDO regulator IDs */ +#define BCM590XX_REG_RFLDO 0 +#define BCM590XX_REG_CAMLDO1 1 +#define BCM590XX_REG_CAMLDO2 2 +#define BCM590XX_REG_SIMLDO1 3 +#define BCM590XX_REG_SIMLDO2 4 +#define BCM590XX_REG_SDLDO 5 +#define BCM590XX_REG_SDXLDO 6 +#define BCM590XX_REG_MMCLDO1 7 +#define BCM590XX_REG_MMCLDO2 8 +#define BCM590XX_REG_AUDLDO 9 +#define BCM590XX_REG_MICLDO 10 +#define BCM590XX_REG_USBLDO 11 +#define BCM590XX_REG_VIBLDO 12 + +/* DCDC regulator IDs */ +#define BCM590XX_REG_CSR 13 +#define BCM590XX_REG_IOSR1 14 +#define BCM590XX_REG_IOSR2 15 +#define BCM590XX_REG_MSR 16 +#define BCM590XX_REG_SDSR1 17 +#define BCM590XX_REG_SDSR2 18 +#define BCM590XX_REG_VSR 19 + +#define BCM590XX_NUM_REGS 20 + +#define BCM590XX_REG_IS_LDO(n) (n < BCM590XX_REG_CSR) + +struct bcm590xx_board { + struct regulator_init_data *bcm590xx_pmu_init_data[BCM590XX_NUM_REGS]; +}; + +/* LDO group A: supported voltages in microvolts */ +static const unsigned int ldo_a_table[] = { + 1200000, 1800000, 2500000, 2700000, 2800000, + 2900000, 3000000, 3300000, +}; + +/* LDO group C: supported voltages in microvolts */ +static const unsigned int ldo_c_table[] = { + 3100000, 1800000, 2500000, 2700000, 2800000, + 2900000, 3000000, 3300000, +}; + +/* DCDC group CSR: supported voltages in microvolts */ +static const struct regulator_linear_range dcdc_csr_ranges[] = { + REGULATOR_LINEAR_RANGE(860000, 2, 50, 10000), + REGULATOR_LINEAR_RANGE(1360000, 51, 55, 20000), + REGULATOR_LINEAR_RANGE(900000, 56, 63, 0), +}; + +/* DCDC group IOSR1: supported voltages in microvolts */ +static const struct regulator_linear_range dcdc_iosr1_ranges[] = { + REGULATOR_LINEAR_RANGE(860000, 2, 51, 10000), + REGULATOR_LINEAR_RANGE(1500000, 52, 52, 0), + REGULATOR_LINEAR_RANGE(1800000, 53, 53, 0), + REGULATOR_LINEAR_RANGE(900000, 54, 63, 0), +}; + +/* DCDC group SDSR1: supported voltages in microvolts */ +static const struct regulator_linear_range dcdc_sdsr1_ranges[] = { + REGULATOR_LINEAR_RANGE(860000, 2, 50, 10000), + REGULATOR_LINEAR_RANGE(1340000, 51, 51, 0), + REGULATOR_LINEAR_RANGE(900000, 52, 63, 0), +}; + +struct bcm590xx_info { + const char *name; + const char *vin_name; + u8 n_voltages; + const unsigned int *volt_table; + u8 n_linear_ranges; + const struct regulator_linear_range *linear_ranges; +}; + +#define BCM590XX_REG_TABLE(_name, _table) \ + { \ + .name = #_name, \ + .n_voltages = ARRAY_SIZE(_table), \ + .volt_table = _table, \ + } + +#define BCM590XX_REG_RANGES(_name, _ranges) \ + { \ + .name = #_name, \ + .n_linear_ranges = ARRAY_SIZE(_ranges), \ + .linear_ranges = _ranges, \ + } + +static struct bcm590xx_info bcm590xx_regs[] = { + BCM590XX_REG_TABLE(rfldo, ldo_a_table), + BCM590XX_REG_TABLE(camldo1, ldo_c_table), + BCM590XX_REG_TABLE(camldo2, ldo_c_table), + BCM590XX_REG_TABLE(simldo1, ldo_a_table), + BCM590XX_REG_TABLE(simldo2, ldo_a_table), + BCM590XX_REG_TABLE(sdldo, ldo_c_table), + BCM590XX_REG_TABLE(sdxldo, ldo_a_table), + BCM590XX_REG_TABLE(mmcldo1, ldo_a_table), + BCM590XX_REG_TABLE(mmcldo2, ldo_a_table), + BCM590XX_REG_TABLE(audldo, ldo_a_table), + BCM590XX_REG_TABLE(micldo, ldo_a_table), + BCM590XX_REG_TABLE(usbldo, ldo_a_table), + BCM590XX_REG_TABLE(vibldo, ldo_c_table), + BCM590XX_REG_RANGES(csr, dcdc_csr_ranges), + BCM590XX_REG_RANGES(iosr1, dcdc_iosr1_ranges), + BCM590XX_REG_RANGES(iosr2, dcdc_iosr1_ranges), + BCM590XX_REG_RANGES(msr, dcdc_iosr1_ranges), + BCM590XX_REG_RANGES(sdsr1, dcdc_sdsr1_ranges), + BCM590XX_REG_RANGES(sdsr2, dcdc_iosr1_ranges), + BCM590XX_REG_RANGES(vsr, dcdc_iosr1_ranges), +}; + +struct bcm590xx_reg { + struct regulator_desc *desc; + struct bcm590xx *mfd; + struct bcm590xx_info **info; +}; + +static int bcm590xx_get_vsel_register(int id) +{ + if (BCM590XX_REG_IS_LDO(id)) + return BCM590XX_RFLDOCTRL + id; + else + return BCM590XX_CSRVOUT1 + (id - BCM590XX_REG_CSR) * 3; +} + +static int bcm590xx_get_enable_register(int id) +{ + int reg = 0; + + if (BCM590XX_REG_IS_LDO(id)) + reg = BCM590XX_RFLDOPMCTRL1 + id * 2; + else + switch (id) { + case BCM590XX_REG_CSR: + reg = BCM590XX_CSRPMCTRL1; + break; + case BCM590XX_REG_IOSR1: + reg = BCM590XX_IOSR1PMCTRL1; + break; + case BCM590XX_REG_IOSR2: + reg = BCM590XX_IOSR2PMCTRL1; + break; + case BCM590XX_REG_MSR: + reg = BCM590XX_MSRPMCTRL1; + break; + case BCM590XX_REG_SDSR1: + reg = BCM590XX_SDSR1PMCTRL1; + break; + case BCM590XX_REG_SDSR2: + reg = BCM590XX_SDSR2PMCTRL1; + break; + }; + + return reg; +} + +static struct regulator_ops bcm590xx_ops_ldo = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, +}; + +static struct regulator_ops bcm590xx_ops_dcdc = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, +}; + +#define BCM590XX_MATCH(_name, _id) \ + { \ + .name = #_name, \ + .driver_data = (void *)&bcm590xx_regs[BCM590XX_REG_##_id], \ + } + +static struct of_regulator_match bcm590xx_matches[] = { + BCM590XX_MATCH(rfldo, RFLDO), + BCM590XX_MATCH(camldo1, CAMLDO1), + BCM590XX_MATCH(camldo2, CAMLDO2), + BCM590XX_MATCH(simldo1, SIMLDO1), + BCM590XX_MATCH(simldo2, SIMLDO2), + BCM590XX_MATCH(sdldo, SDLDO), + BCM590XX_MATCH(sdxldo, SDXLDO), + BCM590XX_MATCH(mmcldo1, MMCLDO1), + BCM590XX_MATCH(mmcldo2, MMCLDO2), + BCM590XX_MATCH(audldo, AUDLDO), + BCM590XX_MATCH(micldo, MICLDO), + BCM590XX_MATCH(usbldo, USBLDO), + BCM590XX_MATCH(vibldo, VIBLDO), + BCM590XX_MATCH(csr, CSR), + BCM590XX_MATCH(iosr1, IOSR1), + BCM590XX_MATCH(iosr2, IOSR2), + BCM590XX_MATCH(msr, MSR), + BCM590XX_MATCH(sdsr1, SDSR1), + BCM590XX_MATCH(sdsr2, SDSR2), + BCM590XX_MATCH(vsr, VSR), +}; + +static struct bcm590xx_board *bcm590xx_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **bcm590xx_reg_matches) +{ + struct bcm590xx_board *data; + struct device_node *np = pdev->dev.parent->of_node; + struct device_node *regulators; + struct of_regulator_match *matches = bcm590xx_matches; + int count = ARRAY_SIZE(bcm590xx_matches); + int idx = 0; + int ret; + + if (!np) { + dev_err(&pdev->dev, "of node not found\n"); + return NULL; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "failed to allocate regulator board data\n"); + return NULL; + } + + np = of_node_get(np); + regulators = of_get_child_by_name(np, "regulators"); + if (!regulators) { + dev_warn(&pdev->dev, "regulator node not found\n"); + return NULL; + } + + ret = of_regulator_match(&pdev->dev, regulators, matches, count); + of_node_put(regulators); + if (ret < 0) { + dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", + ret); + return NULL; + } + + *bcm590xx_reg_matches = matches; + + for (idx = 0; idx < count; idx++) { + if (!matches[idx].init_data || !matches[idx].of_node) + continue; + + data->bcm590xx_pmu_init_data[idx] = matches[idx].init_data; + } + + return data; +} + +static int bcm590xx_probe(struct platform_device *pdev) +{ + struct bcm590xx *bcm590xx = dev_get_drvdata(pdev->dev.parent); + struct bcm590xx_board *pmu_data = NULL; + struct bcm590xx_reg *pmu; + struct regulator_config config = { }; + struct bcm590xx_info *info; + struct regulator_init_data *reg_data; + struct regulator_dev *rdev; + struct of_regulator_match *bcm590xx_reg_matches = NULL; + int i; + + pmu_data = bcm590xx_parse_dt_reg_data(pdev, + &bcm590xx_reg_matches); + + pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL); + if (!pmu) { + dev_err(&pdev->dev, "Memory allocation failed for pmu\n"); + return -ENOMEM; + } + + pmu->mfd = bcm590xx; + + platform_set_drvdata(pdev, pmu); + + pmu->desc = devm_kzalloc(&pdev->dev, BCM590XX_NUM_REGS * + sizeof(struct regulator_desc), GFP_KERNEL); + if (!pmu->desc) { + dev_err(&pdev->dev, "Memory alloc fails for desc\n"); + return -ENOMEM; + } + + pmu->info = devm_kzalloc(&pdev->dev, BCM590XX_NUM_REGS * + sizeof(struct bcm590xx_info *), GFP_KERNEL); + if (!pmu->info) { + dev_err(&pdev->dev, "Memory alloc fails for info\n"); + return -ENOMEM; + } + + info = bcm590xx_regs; + + for (i = 0; i < BCM590XX_NUM_REGS; i++, info++) { + if (pmu_data) + reg_data = pmu_data->bcm590xx_pmu_init_data[i]; + else + reg_data = NULL; + + /* Register the regulators */ + pmu->info[i] = info; + + pmu->desc[i].name = info->name; + pmu->desc[i].supply_name = info->vin_name; + pmu->desc[i].id = i; + pmu->desc[i].volt_table = info->volt_table; + pmu->desc[i].n_voltages = info->n_voltages; + pmu->desc[i].linear_ranges = info->linear_ranges; + pmu->desc[i].n_linear_ranges = info->n_linear_ranges; + + if (BCM590XX_REG_IS_LDO(i)) { + pmu->desc[i].ops = &bcm590xx_ops_ldo; + pmu->desc[i].vsel_mask = BCM590XX_LDO_VSEL_MASK; + } else { + pmu->desc[i].ops = &bcm590xx_ops_dcdc; + pmu->desc[i].vsel_mask = BCM590XX_SR_VSEL_MASK; + } + + pmu->desc[i].vsel_reg = bcm590xx_get_vsel_register(i); + pmu->desc[i].enable_is_inverted = true; + pmu->desc[i].enable_mask = BCM590XX_REG_ENABLE; + pmu->desc[i].enable_reg = bcm590xx_get_enable_register(i); + pmu->desc[i].type = REGULATOR_VOLTAGE; + pmu->desc[i].owner = THIS_MODULE; + + config.dev = bcm590xx->dev; + config.init_data = reg_data; + config.driver_data = pmu; + config.regmap = bcm590xx->regmap; + + if (bcm590xx_reg_matches) + config.of_node = bcm590xx_reg_matches[i].of_node; + + rdev = devm_regulator_register(&pdev->dev, &pmu->desc[i], + &config); + if (IS_ERR(rdev)) { + dev_err(bcm590xx->dev, + "failed to register %s regulator\n", + pdev->name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static struct platform_driver bcm590xx_regulator_driver = { + .driver = { + .name = "bcm590xx-vregs", + .owner = THIS_MODULE, + }, + .probe = bcm590xx_probe, +}; +module_platform_driver(bcm590xx_regulator_driver); + +MODULE_AUTHOR("Matt Porter <mporter@linaro.org>"); +MODULE_DESCRIPTION("BCM590xx voltage regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bcm590xx-vregs"); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d1ac4caaf1b0..bac485acc7f3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -953,6 +953,8 @@ static int machine_constraints_current(struct regulator_dev *rdev, return 0; } +static int _regulator_do_enable(struct regulator_dev *rdev); + /** * set_machine_constraints - sets regulator constraints * @rdev: regulator source @@ -1013,10 +1015,9 @@ static int set_machine_constraints(struct regulator_dev *rdev, /* If the constraints say the regulator should be on at this point * and we have control then make sure it is enabled. */ - if ((rdev->constraints->always_on || rdev->constraints->boot_on) && - ops->enable) { - ret = ops->enable(rdev); - if (ret < 0) { + if (rdev->constraints->always_on || rdev->constraints->boot_on) { + ret = _regulator_do_enable(rdev); + if (ret < 0 && ret != -EINVAL) { rdev_err(rdev, "failed to enable\n"); goto out; } @@ -1907,8 +1908,6 @@ static int _regulator_do_disable(struct regulator_dev *rdev) trace_regulator_disable_complete(rdev_get_name(rdev)); - _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, - NULL); return 0; } @@ -1932,6 +1931,8 @@ static int _regulator_disable(struct regulator_dev *rdev) rdev_err(rdev, "failed to disable\n"); return ret; } + _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, + NULL); } rdev->use_count = 0; @@ -1984,20 +1985,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev) { int ret = 0; - /* force disable */ - if (rdev->desc->ops->disable) { - /* ah well, who wants to live forever... */ - ret = rdev->desc->ops->disable(rdev); - if (ret < 0) { - rdev_err(rdev, "failed to force disable\n"); - return ret; - } - /* notify other consumers that power has been forced off */ - _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | - REGULATOR_EVENT_DISABLE, NULL); + ret = _regulator_do_disable(rdev); + if (ret < 0) { + rdev_err(rdev, "failed to force disable\n"); + return ret; } - return ret; + _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | + REGULATOR_EVENT_DISABLE, NULL); + + return 0; } /** @@ -2402,6 +2399,7 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) struct regulator_dev *rdev = regulator->rdev; int ret = 0; int old_min_uV, old_max_uV; + int current_uV; mutex_lock(&rdev->mutex); @@ -2412,6 +2410,19 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) if (regulator->min_uV == min_uV && regulator->max_uV == max_uV) goto out; + /* If we're trying to set a range that overlaps the current voltage, + * return succesfully even though the regulator does not support + * changing the voltage. + */ + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + current_uV = _regulator_get_voltage(rdev); + if (min_uV <= current_uV && current_uV <= max_uV) { + regulator->min_uV = min_uV; + regulator->max_uV = max_uV; + goto out; + } + } + /* sanity check */ if (!rdev->desc->ops->set_voltage && !rdev->desc->ops->set_voltage_sel) { @@ -3630,23 +3641,18 @@ int regulator_suspend_finish(void) mutex_lock(®ulator_list_mutex); list_for_each_entry(rdev, ®ulator_list, list) { - struct regulator_ops *ops = rdev->desc->ops; - mutex_lock(&rdev->mutex); - if ((rdev->use_count > 0 || rdev->constraints->always_on) && - ops->enable) { - error = ops->enable(rdev); + if (rdev->use_count > 0 || rdev->constraints->always_on) { + error = _regulator_do_enable(rdev); if (error) ret = error; } else { if (!have_full_constraints()) goto unlock; - if (!ops->disable) - goto unlock; if (!_regulator_is_enabled(rdev)) goto unlock; - error = ops->disable(rdev); + error = _regulator_do_disable(rdev); if (error) ret = error; } @@ -3820,7 +3826,7 @@ static int __init regulator_init_complete(void) ops = rdev->desc->ops; c = rdev->constraints; - if (!ops->disable || (c && c->always_on)) + if (c && c->always_on) continue; mutex_lock(&rdev->mutex); @@ -3841,7 +3847,7 @@ static int __init regulator_init_complete(void) /* We log since this may kill the system if it * goes wrong. */ rdev_info(rdev, "disabling\n"); - ret = ops->disable(rdev); + ret = _regulator_do_disable(rdev); if (ret != 0) rdev_err(rdev, "couldn't disable: %d\n", ret); } else { diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c index 3adeaeffc485..fdb6ea8ae7e6 100644 --- a/drivers/regulator/da9052-regulator.c +++ b/drivers/regulator/da9052-regulator.c @@ -240,6 +240,31 @@ static int da9052_regulator_set_voltage_sel(struct regulator_dev *rdev, return ret; } +static int da9052_regulator_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_sel, + unsigned int new_sel) +{ + struct da9052_regulator *regulator = rdev_get_drvdata(rdev); + struct da9052_regulator_info *info = regulator->info; + int id = rdev_get_id(rdev); + int ret = 0; + + /* The DVC controlled LDOs and DCDCs ramp with 6.25mV/µs after enabling + * the activate bit. + */ + switch (id) { + case DA9052_ID_BUCK1: + case DA9052_ID_BUCK2: + case DA9052_ID_BUCK3: + case DA9052_ID_LDO2: + case DA9052_ID_LDO3: + ret = (new_sel - old_sel) * info->step_uV / 6250; + break; + } + + return ret; +} + static struct regulator_ops da9052_dcdc_ops = { .get_current_limit = da9052_dcdc_get_current_limit, .set_current_limit = da9052_dcdc_set_current_limit, @@ -248,6 +273,7 @@ static struct regulator_ops da9052_dcdc_ops = { .map_voltage = da9052_map_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = da9052_regulator_set_voltage_sel, + .set_voltage_time_sel = da9052_regulator_set_voltage_time_sel, .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -258,6 +284,7 @@ static struct regulator_ops da9052_ldo_ops = { .map_voltage = da9052_map_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = da9052_regulator_set_voltage_sel, + .set_voltage_time_sel = da9052_regulator_set_voltage_time_sel, .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -401,7 +428,7 @@ static int da9052_regulator_probe(struct platform_device *pdev) if (!nproot) return -ENODEV; - nproot = of_find_node_by_name(nproot, "regulators"); + nproot = of_get_child_by_name(nproot, "regulators"); if (!nproot) return -ENODEV; diff --git a/drivers/regulator/da9055-regulator.c b/drivers/regulator/da9055-regulator.c index b14ebdad5dd2..9516317e1a9f 100644 --- a/drivers/regulator/da9055-regulator.c +++ b/drivers/regulator/da9055-regulator.c @@ -19,6 +19,8 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/of.h> +#include <linux/regulator/of_regulator.h> #include <linux/mfd/da9055/core.h> #include <linux/mfd/da9055/reg.h> @@ -446,6 +448,9 @@ static int da9055_gpio_init(struct da9055_regulator *regulator, struct da9055_regulator_info *info = regulator->info; int ret = 0; + if (!pdata) + return 0; + if (pdata->gpio_ren && pdata->gpio_ren[id]) { char name[18]; int gpio_mux = pdata->gpio_ren[id]; @@ -530,6 +535,59 @@ static inline struct da9055_regulator_info *find_regulator_info(int id) return NULL; } +#ifdef CONFIG_OF +static struct of_regulator_match da9055_reg_matches[] = { + { .name = "BUCK1", }, + { .name = "BUCK2", }, + { .name = "LDO1", }, + { .name = "LDO2", }, + { .name = "LDO3", }, + { .name = "LDO4", }, + { .name = "LDO5", }, + { .name = "LDO6", }, +}; + +static int da9055_regulator_dt_init(struct platform_device *pdev, + struct da9055_regulator *regulator, + struct regulator_config *config, + int regid) +{ + struct device_node *nproot, *np; + int ret; + + nproot = of_node_get(pdev->dev.parent->of_node); + if (!nproot) + return -ENODEV; + + np = of_get_child_by_name(nproot, "regulators"); + if (!np) + return -ENODEV; + + ret = of_regulator_match(&pdev->dev, np, &da9055_reg_matches[regid], 1); + of_node_put(nproot); + if (ret < 0) { + dev_err(&pdev->dev, "Error matching regulator: %d\n", ret); + return ret; + } + + config->init_data = da9055_reg_matches[regid].init_data; + config->of_node = da9055_reg_matches[regid].of_node; + + if (!config->of_node) + return -ENODEV; + + return 0; +} +#else +static inline int da9055_regulator_dt_init(struct platform_device *pdev, + struct da9055_regulator *regulator, + struct regulator_config *config, + int regid) +{ + return -ENODEV; +} +#endif /* CONFIG_OF */ + static int da9055_regulator_probe(struct platform_device *pdev) { struct regulator_config config = { }; @@ -538,9 +596,6 @@ static int da9055_regulator_probe(struct platform_device *pdev) struct da9055_pdata *pdata = dev_get_platdata(da9055->dev); int ret, irq; - if (pdata == NULL || pdata->regulators[pdev->id] == NULL) - return -ENODEV; - regulator = devm_kzalloc(&pdev->dev, sizeof(struct da9055_regulator), GFP_KERNEL); if (!regulator) @@ -557,8 +612,14 @@ static int da9055_regulator_probe(struct platform_device *pdev) config.driver_data = regulator; config.regmap = da9055->regmap; - if (pdata && pdata->regulators) + if (pdata && pdata->regulators) { config.init_data = pdata->regulators[pdev->id]; + } else { + ret = da9055_regulator_dt_init(pdev, regulator, &config, + pdev->id); + if (ret < 0) + return ret; + } ret = da9055_gpio_init(regulator, &config, pdata, pdev->id); if (ret < 0) diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c index 91e99a2c8dc1..7c9461d13313 100644 --- a/drivers/regulator/da9063-regulator.c +++ b/drivers/regulator/da9063-regulator.c @@ -365,7 +365,7 @@ static int da9063_set_suspend_voltage(struct regulator_dev *rdev, int uV) sel = regulator_map_voltage_linear(rdev, uV, uV); if (sel < 0) - return -EINVAL; + return sel; sel <<= ffs(rdev->desc->vsel_mask) - 1; @@ -666,7 +666,7 @@ static struct da9063_regulators_pdata *da9063_parse_regulators_dt( struct device_node *node; int i, n, num; - node = of_find_node_by_name(pdev->dev.parent->of_node, "regulators"); + node = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); if (!node) { dev_err(&pdev->dev, "Regulators device node not found\n"); return ERR_PTR(-ENODEV); @@ -674,6 +674,7 @@ static struct da9063_regulators_pdata *da9063_parse_regulators_dt( num = of_regulator_match(&pdev->dev, node, da9063_matches, ARRAY_SIZE(da9063_matches)); + of_node_put(node); if (num < 0) { dev_err(&pdev->dev, "Failed to match regulators\n"); return ERR_PTR(-EINVAL); @@ -710,7 +711,7 @@ static struct da9063_regulators_pdata *da9063_parse_regulators_dt( struct platform_device *pdev, struct of_regulator_match **da9063_reg_matches) { - da9063_reg_matches = NULL; + *da9063_reg_matches = NULL; return ERR_PTR(-ENODEV); } #endif @@ -756,7 +757,7 @@ static int da9063_regulator_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Error while reading BUCKs configuration\n"); - return -EIO; + return ret; } bcores_merged = val & DA9063_BCORE_MERGE; bmem_bio_merged = val & DA9063_BUCK_MERGE; @@ -775,10 +776,8 @@ static int da9063_regulator_probe(struct platform_device *pdev) size = sizeof(struct da9063_regulators) + n_regulators * sizeof(struct da9063_regulator); regulators = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!regulators) { - dev_err(&pdev->dev, "No memory for regulators\n"); + if (!regulators) return -ENOMEM; - } regulators->n_regulators = n_regulators; platform_set_drvdata(pdev, regulators); diff --git a/drivers/regulator/da9210-regulator.c b/drivers/regulator/da9210-regulator.c index 6f5ecbe1132e..7a320dd11c46 100644 --- a/drivers/regulator/da9210-regulator.c +++ b/drivers/regulator/da9210-regulator.c @@ -134,11 +134,8 @@ static int da9210_i2c_probe(struct i2c_client *i2c, int error; chip = devm_kzalloc(&i2c->dev, sizeof(struct da9210), GFP_KERNEL); - if (NULL == chip) { - dev_err(&i2c->dev, - "Cannot kzalloc memory for regulator structure\n"); + if (!chip) return -ENOMEM; - } chip->regmap = devm_regmap_init_i2c(i2c, &da9210_regmap_config); if (IS_ERR(chip->regmap)) { diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c index 846acf240e48..617c1adca816 100644 --- a/drivers/regulator/db8500-prcmu.c +++ b/drivers/regulator/db8500-prcmu.c @@ -263,6 +263,8 @@ dbx500_regulator_info[DB8500_NUM_REGULATORS] = { .ops = &db8500_regulator_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .fixed_uV = 1800000, + .n_voltages = 1, }, .exclude_from_power_state = true, }, diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c index ce89f7848a57..2d16b9f16de7 100644 --- a/drivers/regulator/dbx500-prcmu.c +++ b/drivers/regulator/dbx500-prcmu.c @@ -78,6 +78,7 @@ static struct ux500_regulator_debug { void ux500_regulator_suspend_debug(void) { int i; + for (i = 0; i < rdebug.num_regulators; i++) rdebug.state_before_suspend[i] = rdebug.regulator_array[i].is_enabled; @@ -86,6 +87,7 @@ void ux500_regulator_suspend_debug(void) void ux500_regulator_resume_debug(void) { int i; + for (i = 0; i < rdebug.num_regulators; i++) rdebug.state_after_suspend[i] = rdebug.regulator_array[i].is_enabled; @@ -127,9 +129,9 @@ static int ux500_regulator_status_print(struct seq_file *s, void *p) int i; /* print dump header */ - err = seq_printf(s, "ux500-regulator status:\n"); + err = seq_puts(s, "ux500-regulator status:\n"); if (err < 0) - dev_err(dev, "seq_printf overflow\n"); + dev_err(dev, "seq_puts overflow\n"); err = seq_printf(s, "%31s : %8s : %8s\n", "current", "before", "after"); @@ -202,18 +204,12 @@ ux500_regulator_debug_init(struct platform_device *pdev, rdebug.num_regulators = num_regulators; rdebug.state_before_suspend = kzalloc(num_regulators, GFP_KERNEL); - if (!rdebug.state_before_suspend) { - dev_err(&pdev->dev, - "could not allocate memory for saving state\n"); + if (!rdebug.state_before_suspend) goto exit_destroy_power_state; - } rdebug.state_after_suspend = kzalloc(num_regulators, GFP_KERNEL); - if (!rdebug.state_after_suspend) { - dev_err(&pdev->dev, - "could not allocate memory for saving state\n"); + if (!rdebug.state_after_suspend) goto exit_free; - } dbx500_regulator_testcase(regulator_info, num_regulators); return 0; diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c index df9f42524abb..2436db9e2ca3 100644 --- a/drivers/regulator/dummy.c +++ b/drivers/regulator/dummy.c @@ -25,7 +25,11 @@ struct regulator_dev *dummy_regulator_rdev; -static struct regulator_init_data dummy_initdata; +static struct regulator_init_data dummy_initdata = { + .constraints = { + .always_on = 1, + }, +}; static struct regulator_ops dummy_ops; diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c index 7ca3d9e3b0fe..714fd9a89aa1 100644 --- a/drivers/regulator/fan53555.c +++ b/drivers/regulator/fan53555.c @@ -90,11 +90,11 @@ static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV) return 0; ret = regulator_map_voltage_linear(rdev, uV, uV); if (ret < 0) - return -EINVAL; + return ret; ret = regmap_update_bits(di->regmap, di->sleep_reg, VSEL_NSEL_MASK, ret); if (ret < 0) - return -EINVAL; + return ret; /* Cache the sleep voltage setting. * Might not be the real voltage which is rounded */ di->sleep_vol_cache = uV; @@ -244,10 +244,9 @@ static int fan53555_regulator_probe(struct i2c_client *client, di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info), GFP_KERNEL); - if (!di) { - dev_err(&client->dev, "Failed to allocate device info data!\n"); + if (!di) return -ENOMEM; - } + di->regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config); if (IS_ERR(di->regmap)) { dev_err(&client->dev, "Failed to allocate regmap!\n"); @@ -260,14 +259,14 @@ static int fan53555_regulator_probe(struct i2c_client *client, ret = regmap_read(di->regmap, FAN53555_ID1, &val); if (ret < 0) { dev_err(&client->dev, "Failed to get chip ID!\n"); - return -ENODEV; + return ret; } di->chip_id = val & DIE_ID; /* Get chip revision */ ret = regmap_read(di->regmap, FAN53555_ID2, &val); if (ret < 0) { dev_err(&client->dev, "Failed to get chip Rev!\n"); - return -ENODEV; + return ret; } di->chip_rev = val & DIE_REV; dev_info(&client->dev, "FAN53555 Option[%d] Rev[%d] Detected!\n", diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 5ea64b94341c..c61f7e97e4f8 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -130,17 +130,15 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data), GFP_KERNEL); - if (drvdata == NULL) { - dev_err(&pdev->dev, "Failed to allocate device data\n"); - ret = -ENOMEM; - goto err; - } + if (!drvdata) + return -ENOMEM; - drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); + drvdata->desc.name = devm_kstrdup(&pdev->dev, + config->supply_name, + GFP_KERNEL); if (drvdata->desc.name == NULL) { dev_err(&pdev->dev, "Failed to allocate supply name\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } drvdata->desc.type = REGULATOR_VOLTAGE; drvdata->desc.owner = THIS_MODULE; @@ -149,13 +147,13 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.enable_time = config->startup_delay; if (config->input_supply) { - drvdata->desc.supply_name = kstrdup(config->input_supply, - GFP_KERNEL); + drvdata->desc.supply_name = devm_kstrdup(&pdev->dev, + config->input_supply, + GFP_KERNEL); if (!drvdata->desc.supply_name) { dev_err(&pdev->dev, "Failed to allocate input supply\n"); - ret = -ENOMEM; - goto err_name; + return -ENOMEM; } } @@ -186,11 +184,12 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) cfg.driver_data = drvdata; cfg.of_node = pdev->dev.of_node; - drvdata->dev = regulator_register(&drvdata->desc, &cfg); + drvdata->dev = devm_regulator_register(&pdev->dev, &drvdata->desc, + &cfg); if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); - goto err_input; + return ret; } platform_set_drvdata(pdev, drvdata); @@ -199,24 +198,6 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.fixed_uV); return 0; - -err_input: - kfree(drvdata->desc.supply_name); -err_name: - kfree(drvdata->desc.name); -err: - return ret; -} - -static int reg_fixed_voltage_remove(struct platform_device *pdev) -{ - struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); - - regulator_unregister(drvdata->dev); - kfree(drvdata->desc.supply_name); - kfree(drvdata->desc.name); - - return 0; } #if defined(CONFIG_OF) @@ -229,7 +210,6 @@ MODULE_DEVICE_TABLE(of, fixed_of_match); static struct platform_driver regulator_fixed_voltage_driver = { .probe = reg_fixed_voltage_probe, - .remove = reg_fixed_voltage_remove, .driver = { .name = "reg-fixed-voltage", .owner = THIS_MODULE, diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index c0a1d00b78c9..989b23b377c0 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -136,7 +136,6 @@ static struct gpio_regulator_config * of_get_gpio_regulator_config(struct device *dev, struct device_node *np) { struct gpio_regulator_config *config; - struct property *prop; const char *regtype; int proplen, gpio, i; int ret; @@ -172,22 +171,35 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np) if (!config->gpios) return ERR_PTR(-ENOMEM); + proplen = of_property_count_u32_elems(np, "gpios-states"); + /* optional property */ + if (proplen < 0) + proplen = 0; + + if (proplen > 0 && proplen != config->nr_gpios) { + dev_warn(dev, "gpios <-> gpios-states mismatch\n"); + proplen = 0; + } + for (i = 0; i < config->nr_gpios; i++) { gpio = of_get_named_gpio(np, "gpios", i); if (gpio < 0) break; config->gpios[i].gpio = gpio; + if (proplen > 0) { + of_property_read_u32_index(np, "gpios-states", i, &ret); + if (ret) + config->gpios[i].flags = GPIOF_OUT_INIT_HIGH; + } } /* Fetch states. */ - prop = of_find_property(np, "states", NULL); - if (!prop) { + proplen = of_property_count_u32_elems(np, "states"); + if (proplen < 0) { dev_err(dev, "No 'states' property found\n"); return ERR_PTR(-EINVAL); } - proplen = prop->length / sizeof(int); - config->states = devm_kzalloc(dev, sizeof(struct gpio_regulator_state) * (proplen / 2), @@ -196,10 +208,10 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np) return ERR_PTR(-ENOMEM); for (i = 0; i < proplen / 2; i++) { - config->states[i].value = - be32_to_cpup((int *)prop->value + (i * 2)); - config->states[i].gpios = - be32_to_cpup((int *)prop->value + (i * 2 + 1)); + of_property_read_u32_index(np, "states", i * 2, + &config->states[i].value); + of_property_read_u32_index(np, "states", i * 2 + 1, + &config->states[i].gpios); } config->nr_states = i; @@ -239,10 +251,8 @@ static int gpio_regulator_probe(struct platform_device *pdev) drvdata = devm_kzalloc(&pdev->dev, sizeof(struct gpio_regulator_data), GFP_KERNEL); - if (drvdata == NULL) { - dev_err(&pdev->dev, "Failed to allocate device data\n"); + if (drvdata == NULL) return -ENOMEM; - } drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); if (drvdata->desc.name == NULL) { diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index e221a271ba56..cbc39096c78d 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -37,10 +37,17 @@ int regulator_is_enabled_regmap(struct regulator_dev *rdev) if (ret != 0) return ret; - if (rdev->desc->enable_is_inverted) - return (val & rdev->desc->enable_mask) == 0; - else - return (val & rdev->desc->enable_mask) != 0; + val &= rdev->desc->enable_mask; + + if (rdev->desc->enable_is_inverted) { + if (rdev->desc->enable_val) + return val != rdev->desc->enable_val; + return val == 0; + } else { + if (rdev->desc->enable_val) + return val == rdev->desc->enable_val; + return val != 0; + } } EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap); @@ -57,10 +64,13 @@ int regulator_enable_regmap(struct regulator_dev *rdev) { unsigned int val; - if (rdev->desc->enable_is_inverted) - val = 0; - else - val = rdev->desc->enable_mask; + if (rdev->desc->enable_is_inverted) { + val = rdev->desc->disable_val; + } else { + val = rdev->desc->enable_val; + if (!val) + val = rdev->desc->enable_mask; + } return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, rdev->desc->enable_mask, val); @@ -80,10 +90,13 @@ int regulator_disable_regmap(struct regulator_dev *rdev) { unsigned int val; - if (rdev->desc->enable_is_inverted) - val = rdev->desc->enable_mask; - else - val = 0; + if (rdev->desc->enable_is_inverted) { + val = rdev->desc->enable_val; + if (!val) + val = rdev->desc->enable_mask; + } else { + val = rdev->desc->disable_val; + } return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, rdev->desc->enable_mask, val); @@ -419,10 +432,13 @@ int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable) { unsigned int val; - if (enable) - val = rdev->desc->bypass_mask; - else - val = 0; + if (enable) { + val = rdev->desc->bypass_val_on; + if (!val) + val = rdev->desc->bypass_mask; + } else { + val = rdev->desc->bypass_val_off; + } return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg, rdev->desc->bypass_mask, val); diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 3b1102b75071..66fd2330dca0 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -327,7 +327,7 @@ static int lp3971_i2c_read(struct i2c_client *i2c, char reg, int count, return -EIO; ret = i2c_smbus_read_byte_data(i2c, reg); if (ret < 0) - return -EIO; + return ret; *dest = ret; return 0; diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index 2e4734ff79fc..2e022aabd951 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -211,7 +211,7 @@ static int lp872x_get_timestep_usec(struct lp872x *lp) ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val); if (ret) - return -EINVAL; + return ret; val = (val & mask) >> shift; if (val >= size) @@ -229,7 +229,7 @@ static int lp872x_regulator_enable_time(struct regulator_dev *rdev) u8 addr, val; if (time_step_us < 0) - return -EINVAL; + return time_step_us; switch (rid) { case LP8720_ID_LDO1 ... LP8720_ID_BUCK: diff --git a/drivers/regulator/max14577.c b/drivers/regulator/max14577.c index e0619526708c..ed60baaeceec 100644 --- a/drivers/regulator/max14577.c +++ b/drivers/regulator/max14577.c @@ -1,7 +1,7 @@ /* * max14577.c - Regulator driver for the Maxim 14577 * - * Copyright (C) 2013 Samsung Electronics + * Copyright (C) 2013,2014 Samsung Electronics * Krzysztof Kozlowski <k.kozlowski@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -22,12 +22,6 @@ #include <linux/mfd/max14577-private.h> #include <linux/regulator/of_regulator.h> -struct max14577_regulator { - struct device *dev; - struct max14577 *max14577; - struct regulator_dev **regulators; -}; - static int max14577_reg_is_enabled(struct regulator_dev *rdev) { int rid = rdev_get_id(rdev); diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index e242dd316d36..d23d0577754b 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -46,8 +46,6 @@ struct max1586_data { unsigned int v3_curr_sel; unsigned int v6_curr_sel; - - struct regulator_dev *rdev[0]; }; /* @@ -162,14 +160,12 @@ static struct regulator_desc max1586_reg[] = { static int max1586_pmic_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { - struct regulator_dev **rdev; struct max1586_platform_data *pdata = dev_get_platdata(&client->dev); struct regulator_config config = { }; struct max1586_data *max1586; int i, id; - max1586 = devm_kzalloc(&client->dev, sizeof(struct max1586_data) + - sizeof(struct regulator_dev *) * (MAX1586_V6 + 1), + max1586 = devm_kzalloc(&client->dev, sizeof(struct max1586_data), GFP_KERNEL); if (!max1586) return -ENOMEM; @@ -186,8 +182,9 @@ static int max1586_pmic_probe(struct i2c_client *client, max1586->v3_curr_sel = 24; /* 1.3V */ max1586->v6_curr_sel = 0; - rdev = max1586->rdev; for (i = 0; i < pdata->num_subdevs && i <= MAX1586_V6; i++) { + struct regulator_dev *rdev; + id = pdata->subdevs[i].id; if (!pdata->subdevs[i].platform_data) continue; @@ -207,12 +204,12 @@ static int max1586_pmic_probe(struct i2c_client *client, config.init_data = pdata->subdevs[i].platform_data; config.driver_data = max1586; - rdev[i] = devm_regulator_register(&client->dev, + rdev = devm_regulator_register(&client->dev, &max1586_reg[id], &config); - if (IS_ERR(rdev[i])) { + if (IS_ERR(rdev)) { dev_err(&client->dev, "failed to register %s\n", max1586_reg[id].name); - return PTR_ERR(rdev[i]); + return PTR_ERR(rdev); } } diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index ae001ccf26f4..ef1af2debbd2 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -65,7 +65,6 @@ enum max77686_ramp_rate { }; struct max77686_data { - struct regulator_dev *rdev[MAX77686_REGULATORS]; unsigned int opmode[MAX77686_REGULATORS]; }; @@ -400,7 +399,7 @@ static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev, unsigned int i; pmic_np = iodev->dev->of_node; - regulators_np = of_find_node_by_name(pmic_np, "voltage-regulators"); + regulators_np = of_get_child_by_name(pmic_np, "voltage-regulators"); if (!regulators_np) { dev_err(&pdev->dev, "could not find regulators sub-node\n"); return -EINVAL; @@ -410,8 +409,7 @@ static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev, rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * pdata->num_regulators, GFP_KERNEL); if (!rdata) { - dev_err(&pdev->dev, - "could not allocate memory for regulator data\n"); + of_node_put(regulators_np); return -ENOMEM; } @@ -425,6 +423,7 @@ static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev, } pdata->regulators = rdata; + of_node_put(regulators_np); return 0; } @@ -474,16 +473,18 @@ static int max77686_pmic_probe(struct platform_device *pdev) platform_set_drvdata(pdev, max77686); for (i = 0; i < MAX77686_REGULATORS; i++) { + struct regulator_dev *rdev; + config.init_data = pdata->regulators[i].initdata; config.of_node = pdata->regulators[i].of_node; max77686->opmode[i] = regulators[i].enable_mask; - max77686->rdev[i] = devm_regulator_register(&pdev->dev, + rdev = devm_regulator_register(&pdev->dev, ®ulators[i], &config); - if (IS_ERR(max77686->rdev[i])) { + if (IS_ERR(rdev)) { dev_err(&pdev->dev, "regulator init failed for %d\n", i); - return PTR_ERR(max77686->rdev[i]); + return PTR_ERR(rdev); } } diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index 5fb899f461d0..653a58b49cdf 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c @@ -34,13 +34,6 @@ #define CHGIN_ILIM_STEP_20mA 20000 -struct max77693_pmic_dev { - struct device *dev; - struct max77693_dev *iodev; - int num_regulators; - struct regulator_dev **rdev; -}; - /* CHARGER regulator ops */ /* CHARGER regulator uses two bits for enabling */ static int max77693_chg_is_enabled(struct regulator_dev *rdev) @@ -170,19 +163,22 @@ static int max77693_pmic_dt_parse_rdata(struct device *dev, struct max77693_regulator_data *tmp; int i, matched = 0; - np = of_find_node_by_name(dev->parent->of_node, "regulators"); + np = of_get_child_by_name(dev->parent->of_node, "regulators"); if (!np) return -EINVAL; rmatch = devm_kzalloc(dev, sizeof(*rmatch) * ARRAY_SIZE(regulators), GFP_KERNEL); - if (!rmatch) + if (!rmatch) { + of_node_put(np); return -ENOMEM; + } for (i = 0; i < ARRAY_SIZE(regulators); i++) rmatch[i].name = regulators[i].name; matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(regulators)); + of_node_put(np); if (matched <= 0) return matched; *rdata = devm_kzalloc(dev, sizeof(**rdata) * matched, GFP_KERNEL); @@ -229,7 +225,6 @@ static int max77693_pmic_init_rdata(struct device *dev, static int max77693_pmic_probe(struct platform_device *pdev) { struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max77693_pmic_dev *max77693_pmic; struct max77693_regulator_data *rdata = NULL; int num_rdata, i; struct regulator_config config; @@ -240,39 +235,22 @@ static int max77693_pmic_probe(struct platform_device *pdev) return -ENODEV; } - max77693_pmic = devm_kzalloc(&pdev->dev, - sizeof(struct max77693_pmic_dev), - GFP_KERNEL); - if (!max77693_pmic) - return -ENOMEM; - - max77693_pmic->rdev = devm_kzalloc(&pdev->dev, - sizeof(struct regulator_dev *) * num_rdata, - GFP_KERNEL); - if (!max77693_pmic->rdev) - return -ENOMEM; - - max77693_pmic->dev = &pdev->dev; - max77693_pmic->iodev = iodev; - max77693_pmic->num_regulators = num_rdata; - config.dev = &pdev->dev; config.regmap = iodev->regmap; - config.driver_data = max77693_pmic; - platform_set_drvdata(pdev, max77693_pmic); - for (i = 0; i < max77693_pmic->num_regulators; i++) { + for (i = 0; i < num_rdata; i++) { int id = rdata[i].id; + struct regulator_dev *rdev; config.init_data = rdata[i].initdata; config.of_node = rdata[i].of_node; - max77693_pmic->rdev[i] = devm_regulator_register(&pdev->dev, + rdev = devm_regulator_register(&pdev->dev, ®ulators[id], &config); - if (IS_ERR(max77693_pmic->rdev[i])) { - dev_err(max77693_pmic->dev, + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to initialize regulator-%d\n", id); - return PTR_ERR(max77693_pmic->rdev[i]); + return PTR_ERR(rdev); } } diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index 7f049c92ee52..3172da847d24 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -49,7 +49,6 @@ #define MAX8649_RAMP_DOWN (1 << 1) struct max8649_regulator_info { - struct regulator_dev *regulator; struct device *dev; struct regmap *regmap; @@ -154,6 +153,7 @@ static int max8649_regulator_probe(struct i2c_client *client, { struct max8649_platform_data *pdata = dev_get_platdata(&client->dev); struct max8649_regulator_info *info = NULL; + struct regulator_dev *regulator; struct regulator_config config = { }; unsigned int val; unsigned char data; @@ -234,12 +234,12 @@ static int max8649_regulator_probe(struct i2c_client *client, config.driver_data = info; config.regmap = info->regmap; - info->regulator = devm_regulator_register(&client->dev, &dcdc_desc, + regulator = devm_regulator_register(&client->dev, &dcdc_desc, &config); - if (IS_ERR(info->regulator)) { + if (IS_ERR(regulator)) { dev_err(info->dev, "failed to register regulator %s\n", dcdc_desc.name); - return PTR_ERR(info->regulator); + return PTR_ERR(regulator); } return 0; diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index 8d94d3d7f97f..2fc411188794 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -81,16 +81,17 @@ enum { struct max8660 { struct i2c_client *client; u8 shadow_regs[MAX8660_N_REGS]; /* as chip is write only */ - struct regulator_dev *rdev[]; }; static int max8660_write(struct max8660 *max8660, u8 reg, u8 mask, u8 val) { - static const u8 max8660_addresses[MAX8660_N_REGS] = - { 0x10, 0x12, 0x20, 0x23, 0x24, 0x29, 0x2a, 0x32, 0x33, 0x39, 0x80 }; + static const u8 max8660_addresses[MAX8660_N_REGS] = { + 0x10, 0x12, 0x20, 0x23, 0x24, 0x29, 0x2a, 0x32, 0x33, 0x39, 0x80 + }; int ret; u8 reg_val = (max8660->shadow_regs[reg] & mask) | val; + dev_vdbg(&max8660->client->dev, "Writing reg %02x with %02x\n", max8660_addresses[reg], reg_val); @@ -112,6 +113,7 @@ static int max8660_dcdc_is_enabled(struct regulator_dev *rdev) struct max8660 *max8660 = rdev_get_drvdata(rdev); u8 val = max8660->shadow_regs[MAX8660_OVER1]; u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4; + return !!(val & mask); } @@ -119,6 +121,7 @@ static int max8660_dcdc_enable(struct regulator_dev *rdev) { struct max8660 *max8660 = rdev_get_drvdata(rdev); u8 bit = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4; + return max8660_write(max8660, MAX8660_OVER1, 0xff, bit); } @@ -126,15 +129,16 @@ static int max8660_dcdc_disable(struct regulator_dev *rdev) { struct max8660 *max8660 = rdev_get_drvdata(rdev); u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? ~1 : ~4; + return max8660_write(max8660, MAX8660_OVER1, mask, 0); } static int max8660_dcdc_get_voltage_sel(struct regulator_dev *rdev) { struct max8660 *max8660 = rdev_get_drvdata(rdev); - u8 reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2; u8 selector = max8660->shadow_regs[reg]; + return selector; } @@ -207,6 +211,7 @@ static int max8660_ldo67_is_enabled(struct regulator_dev *rdev) struct max8660 *max8660 = rdev_get_drvdata(rdev); u8 val = max8660->shadow_regs[MAX8660_OVER2]; u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4; + return !!(val & mask); } @@ -214,6 +219,7 @@ static int max8660_ldo67_enable(struct regulator_dev *rdev) { struct max8660 *max8660 = rdev_get_drvdata(rdev); u8 bit = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4; + return max8660_write(max8660, MAX8660_OVER2, 0xff, bit); } @@ -221,15 +227,16 @@ static int max8660_ldo67_disable(struct regulator_dev *rdev) { struct max8660 *max8660 = rdev_get_drvdata(rdev); u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? ~2 : ~4; + return max8660_write(max8660, MAX8660_OVER2, mask, 0); } static int max8660_ldo67_get_voltage_sel(struct regulator_dev *rdev) { struct max8660 *max8660 = rdev_get_drvdata(rdev); - u8 shift = (rdev_get_id(rdev) == MAX8660_V6) ? 0 : 4; u8 selector = (max8660->shadow_regs[MAX8660_L12VCR] >> shift) & 0xf; + return selector; } @@ -330,7 +337,7 @@ static int max8660_pdata_from_dt(struct device *dev, struct max8660_subdev_data *sub; struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)]; - np = of_find_node_by_name(dev->of_node, "regulators"); + np = of_get_child_by_name(dev->of_node, "regulators"); if (!np) { dev_err(dev, "missing 'regulators' subnode in DT\n"); return -EINVAL; @@ -340,6 +347,7 @@ static int max8660_pdata_from_dt(struct device *dev, rmatch[i].name = max8660_reg[i].name; matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(rmatch)); + of_node_put(np); if (matched <= 0) return matched; @@ -373,7 +381,6 @@ static inline int max8660_pdata_from_dt(struct device *dev, static int max8660_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { - struct regulator_dev **rdev; struct device *dev = &client->dev; struct max8660_platform_data *pdata = dev_get_platdata(dev); struct regulator_config config = { }; @@ -406,14 +413,11 @@ static int max8660_probe(struct i2c_client *client, return -EINVAL; } - max8660 = devm_kzalloc(dev, sizeof(struct max8660) + - sizeof(struct regulator_dev *) * MAX8660_V_END, - GFP_KERNEL); + max8660 = devm_kzalloc(dev, sizeof(struct max8660), GFP_KERNEL); if (!max8660) return -ENOMEM; max8660->client = client; - rdev = max8660->rdev; if (pdata->en34_is_high) { /* Simulate always on */ @@ -481,6 +485,7 @@ static int max8660_probe(struct i2c_client *client, /* Finally register devices */ for (i = 0; i < pdata->num_subdevs; i++) { + struct regulator_dev *rdev; id = pdata->subdevs[i].id; @@ -489,13 +494,13 @@ static int max8660_probe(struct i2c_client *client, config.of_node = of_node[i]; config.driver_data = max8660; - rdev[i] = devm_regulator_register(&client->dev, + rdev = devm_regulator_register(&client->dev, &max8660_reg[id], &config); - if (IS_ERR(rdev[i])) { - ret = PTR_ERR(rdev[i]); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); dev_err(&client->dev, "failed to register %s\n", max8660_reg[id].name); - return PTR_ERR(rdev[i]); + return PTR_ERR(rdev); } } diff --git a/drivers/regulator/max8907-regulator.c b/drivers/regulator/max8907-regulator.c index 0c5fe6c6ac26..9623e9e290bf 100644 --- a/drivers/regulator/max8907-regulator.c +++ b/drivers/regulator/max8907-regulator.c @@ -34,7 +34,6 @@ struct max8907_regulator { struct regulator_desc desc[MAX8907_NUM_REGULATORS]; - struct regulator_dev *rdev[MAX8907_NUM_REGULATORS]; }; #define REG_MBATT() \ @@ -231,7 +230,7 @@ static int max8907_regulator_parse_dt(struct platform_device *pdev) if (!np) return 0; - regulators = of_find_node_by_name(np, "regulators"); + regulators = of_get_child_by_name(np, "regulators"); if (!regulators) { dev_err(&pdev->dev, "regulators node not found\n"); return -EINVAL; @@ -292,10 +291,9 @@ static int max8907_regulator_probe(struct platform_device *pdev) return ret; pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); - if (!pmic) { - dev_err(&pdev->dev, "Failed to alloc pmic\n"); + if (!pmic) return -ENOMEM; - } + platform_set_drvdata(pdev, pmic); memcpy(pmic->desc, max8907_regulators, sizeof(pmic->desc)); @@ -311,6 +309,8 @@ static int max8907_regulator_probe(struct platform_device *pdev) } for (i = 0; i < MAX8907_NUM_REGULATORS; i++) { + struct regulator_dev *rdev; + config.dev = pdev->dev.parent; if (pdata) idata = pdata->init_data[i]; @@ -350,13 +350,13 @@ static int max8907_regulator_probe(struct platform_device *pdev) pmic->desc[i].ops = &max8907_out5v_hwctl_ops; } - pmic->rdev[i] = devm_regulator_register(&pdev->dev, + rdev = devm_regulator_register(&pdev->dev, &pmic->desc[i], &config); - if (IS_ERR(pmic->rdev[i])) { + if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register %s regulator\n", pmic->desc[i].name); - return PTR_ERR(pmic->rdev[i]); + return PTR_ERR(rdev); } } diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index 759510789e71..dad2bcd14e96 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -36,9 +36,7 @@ struct max8925_regulator_info { struct regulator_desc desc; - struct regulator_dev *regulator; struct i2c_client *i2c; - struct max8925_chip *chip; int vol_reg; int enable_reg; @@ -251,10 +249,11 @@ static int max8925_regulator_dt_init(struct platform_device *pdev, { struct device_node *nproot, *np; int rcount; + nproot = of_node_get(pdev->dev.parent->of_node); if (!nproot) return -ENODEV; - np = of_find_node_by_name(nproot, "regulators"); + np = of_get_child_by_name(nproot, "regulators"); if (!np) { dev_err(&pdev->dev, "failed to find regulators node\n"); return -ENODEV; @@ -264,7 +263,7 @@ static int max8925_regulator_dt_init(struct platform_device *pdev, &max8925_regulator_matches[ridx], 1); of_node_put(np); if (rcount < 0) - return -ENODEV; + return rcount; config->init_data = max8925_regulator_matches[ridx].init_data; config->of_node = max8925_regulator_matches[ridx].of_node; @@ -303,7 +302,6 @@ static int max8925_regulator_probe(struct platform_device *pdev) return -EINVAL; } ri->i2c = chip->i2c; - ri->chip = chip; config.dev = &pdev->dev; config.driver_data = ri; diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index 788e5ae2af1b..d920f5a32ec8 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -48,9 +48,7 @@ enum { struct max8952_data { struct i2c_client *client; - struct device *dev; struct max8952_platform_data *pdata; - struct regulator_dev *rdev; bool vid0; bool vid1; @@ -59,6 +57,7 @@ struct max8952_data { static int max8952_read_reg(struct max8952_data *max8952, u8 reg) { int ret = i2c_smbus_read_byte_data(max8952->client, reg); + if (ret > 0) ret &= 0xff; @@ -144,10 +143,8 @@ static struct max8952_platform_data *max8952_parse_dt(struct device *dev) int i; pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - if (!pd) { - dev_err(dev, "Failed to allocate platform data\n"); + if (!pd) return NULL; - } pd->gpio_vid0 = of_get_named_gpio(np, "max8952,vid-gpios", 0); pd->gpio_vid1 = of_get_named_gpio(np, "max8952,vid-gpios", 1); @@ -199,6 +196,7 @@ static int max8952_pmic_probe(struct i2c_client *client, struct max8952_platform_data *pdata = dev_get_platdata(&client->dev); struct regulator_config config = { }; struct max8952_data *max8952; + struct regulator_dev *rdev; int ret = 0, err = 0; @@ -219,10 +217,9 @@ static int max8952_pmic_probe(struct i2c_client *client, return -ENOMEM; max8952->client = client; - max8952->dev = &client->dev; max8952->pdata = pdata; - config.dev = max8952->dev; + config.dev = &client->dev; config.init_data = pdata->reg_data; config.driver_data = max8952; config.of_node = client->dev.of_node; @@ -231,11 +228,11 @@ static int max8952_pmic_probe(struct i2c_client *client, if (pdata->reg_data->constraints.boot_on) config.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; - max8952->rdev = regulator_register(®ulator, &config); + rdev = devm_regulator_register(&client->dev, ®ulator, &config); - if (IS_ERR(max8952->rdev)) { - ret = PTR_ERR(max8952->rdev); - dev_err(max8952->dev, "regulator init failed (%d)\n", ret); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(&client->dev, "regulator init failed (%d)\n", ret); return ret; } @@ -263,7 +260,7 @@ static int max8952_pmic_probe(struct i2c_client *client, err = 3; if (err) { - dev_warn(max8952->dev, "VID0/1 gpio invalid: " + dev_warn(&client->dev, "VID0/1 gpio invalid: " "DVS not available.\n"); max8952->vid0 = 0; max8952->vid1 = 0; @@ -274,7 +271,7 @@ static int max8952_pmic_probe(struct i2c_client *client, /* Disable Pulldown of EN only */ max8952_write_reg(max8952, MAX8952_REG_CONTROL, 0x60); - dev_err(max8952->dev, "DVS modes disabled because VID0 and VID1" + dev_err(&client->dev, "DVS modes disabled because VID0 and VID1" " do not have proper controls.\n"); } else { /* @@ -321,9 +318,6 @@ static int max8952_pmic_remove(struct i2c_client *client) { struct max8952_data *max8952 = i2c_get_clientdata(client); struct max8952_platform_data *pdata = max8952->pdata; - struct regulator_dev *rdev = max8952->rdev; - - regulator_unregister(rdev); gpio_free(pdata->gpio_vid0); gpio_free(pdata->gpio_vid1); diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index 892aa1e5b96c..dbedf1768db0 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -93,7 +93,6 @@ struct max8973_chip { struct device *dev; struct regulator_desc desc; - struct regulator_dev *rdev; struct regmap *regmap; bool enable_external_control; int dvs_gpio; @@ -379,10 +378,8 @@ static int max8973_probe(struct i2c_client *client, } max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL); - if (!max) { - dev_err(&client->dev, "Memory allocation for max failed\n"); + if (!max) return -ENOMEM; - } max->regmap = devm_regmap_init_i2c(client, &max8973_regmap_config); if (IS_ERR(max->regmap)) { @@ -474,7 +471,6 @@ static int max8973_probe(struct i2c_client *client, return ret; } - max->rdev = rdev; return 0; } diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index 2d618fc9c1af..90b4c530dee5 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -38,7 +38,6 @@ struct max8997_data { struct device *dev; struct max8997_dev *iodev; int num_regulators; - struct regulator_dev **rdev; int ramp_delay; /* in mV/us */ bool buck1_gpiodvs; @@ -924,7 +923,7 @@ static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev, return -ENODEV; } - regulators_np = of_find_node_by_name(pmic_np, "regulators"); + regulators_np = of_get_child_by_name(pmic_np, "regulators"); if (!regulators_np) { dev_err(&pdev->dev, "could not find regulators sub-node\n"); return -EINVAL; @@ -937,7 +936,6 @@ static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev, pdata->num_regulators, GFP_KERNEL); if (!rdata) { of_node_put(regulators_np); - dev_err(&pdev->dev, "could not allocate memory for regulator data\n"); return -ENOMEM; } @@ -1030,10 +1028,10 @@ static int max8997_pmic_probe(struct platform_device *pdev) struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct max8997_platform_data *pdata = iodev->pdata; struct regulator_config config = { }; - struct regulator_dev **rdev; + struct regulator_dev *rdev; struct max8997_data *max8997; struct i2c_client *i2c; - int i, ret, size, nr_dvs; + int i, ret, nr_dvs; u8 max_buck1 = 0, max_buck2 = 0, max_buck5 = 0; if (!pdata) { @@ -1052,12 +1050,6 @@ static int max8997_pmic_probe(struct platform_device *pdev) if (!max8997) return -ENOMEM; - size = sizeof(struct regulator_dev *) * pdata->num_regulators; - max8997->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!max8997->rdev) - return -ENOMEM; - - rdev = max8997->rdev; max8997->dev = &pdev->dev; max8997->iodev = iodev; max8997->num_regulators = pdata->num_regulators; @@ -1205,12 +1197,12 @@ static int max8997_pmic_probe(struct platform_device *pdev) config.driver_data = max8997; config.of_node = pdata->regulators[i].reg_node; - rdev[i] = devm_regulator_register(&pdev->dev, ®ulators[id], - &config); - if (IS_ERR(rdev[i])) { + rdev = devm_regulator_register(&pdev->dev, ®ulators[id], + &config); + if (IS_ERR(rdev)) { dev_err(max8997->dev, "regulator init failed for %d\n", id); - return PTR_ERR(rdev[i]); + return PTR_ERR(rdev); } } diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index ae3f0656feb0..961091b46557 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -40,7 +40,6 @@ struct max8998_data { struct device *dev; struct max8998_dev *iodev; int num_regulators; - struct regulator_dev **rdev; u8 buck1_vol[4]; /* voltages for selection */ u8 buck2_vol[2]; unsigned int buck1_idx; /* index to last changed voltage */ @@ -674,8 +673,10 @@ static int max8998_pmic_dt_parse_pdata(struct max8998_dev *iodev, rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * pdata->num_regulators, GFP_KERNEL); - if (!rdata) + if (!rdata) { + of_node_put(regulators_np); return -ENOMEM; + } pdata->regulators = rdata; for (i = 0; i < ARRAY_SIZE(regulators); ++i) { @@ -692,6 +693,9 @@ static int max8998_pmic_dt_parse_pdata(struct max8998_dev *iodev, } pdata->num_regulators = rdata - pdata->regulators; + of_node_put(reg_np); + of_node_put(regulators_np); + ret = max8998_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); if (ret) return -EINVAL; @@ -741,10 +745,10 @@ static int max8998_pmic_probe(struct platform_device *pdev) struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct max8998_platform_data *pdata = iodev->pdata; struct regulator_config config = { }; - struct regulator_dev **rdev; + struct regulator_dev *rdev; struct max8998_data *max8998; struct i2c_client *i2c; - int i, ret, size; + int i, ret; unsigned int v; if (!pdata) { @@ -763,12 +767,6 @@ static int max8998_pmic_probe(struct platform_device *pdev) if (!max8998) return -ENOMEM; - size = sizeof(struct regulator_dev *) * pdata->num_regulators; - max8998->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!max8998->rdev) - return -ENOMEM; - - rdev = max8998->rdev; max8998->dev = &pdev->dev; max8998->iodev = iodev; max8998->num_regulators = pdata->num_regulators; @@ -872,13 +870,12 @@ static int max8998_pmic_probe(struct platform_device *pdev) config.init_data = pdata->regulators[i].initdata; config.driver_data = max8998; - rdev[i] = devm_regulator_register(&pdev->dev, - ®ulators[index], &config); - if (IS_ERR(rdev[i])) { - ret = PTR_ERR(rdev[i]); + rdev = devm_regulator_register(&pdev->dev, ®ulators[index], + &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); dev_err(max8998->dev, "regulator %s init failed (%d)\n", regulators[index].name, ret); - rdev[i] = NULL; return ret; } } diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index da4859282302..05b971726ffa 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -167,8 +167,10 @@ int mc13xxx_get_num_regulators_dt(struct platform_device *pdev) struct device_node *parent; int num; - of_node_get(pdev->dev.parent->of_node); - parent = of_find_node_by_name(pdev->dev.parent->of_node, "regulators"); + if (!pdev->dev.parent->of_node) + return -ENODEV; + + parent = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); if (!parent) return -ENODEV; @@ -187,8 +189,10 @@ struct mc13xxx_regulator_init_data *mc13xxx_parse_regulators_dt( struct device_node *parent, *child; int i, parsed = 0; - of_node_get(pdev->dev.parent->of_node); - parent = of_find_node_by_name(pdev->dev.parent->of_node, "regulators"); + if (!pdev->dev.parent->of_node) + return NULL; + + parent = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); if (!parent) return NULL; diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index ab174f20ca11..67e678c4301c 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -56,6 +56,8 @@ #define PFUZE100_VGEN5VOL 0x70 #define PFUZE100_VGEN6VOL 0x71 +enum chips { PFUZE100, PFUZE200 }; + struct pfuze_regulator { struct regulator_desc desc; unsigned char stby_reg; @@ -63,6 +65,7 @@ struct pfuze_regulator { }; struct pfuze_chip { + int chip_id; struct regmap *regmap; struct device *dev; struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR]; @@ -78,21 +81,23 @@ static const int pfuze100_vsnvs[] = { }; static const struct i2c_device_id pfuze_device_id[] = { - {.name = "pfuze100"}, - {}, + {.name = "pfuze100", .driver_data = PFUZE100}, + {.name = "pfuze200", .driver_data = PFUZE200}, + { } }; MODULE_DEVICE_TABLE(i2c, pfuze_device_id); static const struct of_device_id pfuze_dt_ids[] = { - { .compatible = "fsl,pfuze100" }, - {}, + { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100}, + { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200}, + { } }; MODULE_DEVICE_TABLE(of, pfuze_dt_ids); static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) { struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev); - int id = rdev->desc->id; + int id = rdev_get_id(rdev); unsigned int ramp_bits; int ret; @@ -139,14 +144,14 @@ static struct regulator_ops pfuze100_swb_regulator_ops = { }; -#define PFUZE100_FIXED_REG(_name, base, voltage) \ - [PFUZE100_ ## _name] = { \ +#define PFUZE100_FIXED_REG(_chip, _name, base, voltage) \ + [_chip ## _ ## _name] = { \ .desc = { \ .name = #_name, \ .n_voltages = 1, \ .ops = &pfuze100_fixed_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ - .id = PFUZE100_ ## _name, \ + .id = _chip ## _ ## _name, \ .owner = THIS_MODULE, \ .min_uV = (voltage), \ .enable_reg = (base), \ @@ -154,14 +159,14 @@ static struct regulator_ops pfuze100_swb_regulator_ops = { }, \ } -#define PFUZE100_SW_REG(_name, base, min, max, step) \ - [PFUZE100_ ## _name] = { \ +#define PFUZE100_SW_REG(_chip, _name, base, min, max, step) \ + [_chip ## _ ## _name] = { \ .desc = { \ .name = #_name,\ .n_voltages = ((max) - (min)) / (step) + 1, \ .ops = &pfuze100_sw_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ - .id = PFUZE100_ ## _name, \ + .id = _chip ## _ ## _name, \ .owner = THIS_MODULE, \ .min_uV = (min), \ .uV_step = (step), \ @@ -172,14 +177,14 @@ static struct regulator_ops pfuze100_swb_regulator_ops = { .stby_mask = 0x3f, \ } -#define PFUZE100_SWB_REG(_name, base, mask, voltages) \ - [PFUZE100_ ## _name] = { \ +#define PFUZE100_SWB_REG(_chip, _name, base, mask, voltages) \ + [_chip ## _ ## _name] = { \ .desc = { \ .name = #_name, \ .n_voltages = ARRAY_SIZE(voltages), \ .ops = &pfuze100_swb_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ - .id = PFUZE100_ ## _name, \ + .id = _chip ## _ ## _name, \ .owner = THIS_MODULE, \ .volt_table = voltages, \ .vsel_reg = (base), \ @@ -187,14 +192,14 @@ static struct regulator_ops pfuze100_swb_regulator_ops = { }, \ } -#define PFUZE100_VGEN_REG(_name, base, min, max, step) \ - [PFUZE100_ ## _name] = { \ +#define PFUZE100_VGEN_REG(_chip, _name, base, min, max, step) \ + [_chip ## _ ## _name] = { \ .desc = { \ .name = #_name, \ .n_voltages = ((max) - (min)) / (step) + 1, \ .ops = &pfuze100_ldo_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ - .id = PFUZE100_ ## _name, \ + .id = _chip ## _ ## _name, \ .owner = THIS_MODULE, \ .min_uV = (min), \ .uV_step = (step), \ @@ -207,25 +212,45 @@ static struct regulator_ops pfuze100_swb_regulator_ops = { .stby_mask = 0x20, \ } +/* PFUZE100 */ static struct pfuze_regulator pfuze100_regulators[] = { - PFUZE100_SW_REG(SW1AB, PFUZE100_SW1ABVOL, 300000, 1875000, 25000), - PFUZE100_SW_REG(SW1C, PFUZE100_SW1CVOL, 300000, 1875000, 25000), - PFUZE100_SW_REG(SW2, PFUZE100_SW2VOL, 400000, 1975000, 25000), - PFUZE100_SW_REG(SW3A, PFUZE100_SW3AVOL, 400000, 1975000, 25000), - PFUZE100_SW_REG(SW3B, PFUZE100_SW3BVOL, 400000, 1975000, 25000), - PFUZE100_SW_REG(SW4, PFUZE100_SW4VOL, 400000, 1975000, 25000), - PFUZE100_SWB_REG(SWBST, PFUZE100_SWBSTCON1, 0x3 , pfuze100_swbst), - PFUZE100_SWB_REG(VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), - PFUZE100_FIXED_REG(VREFDDR, PFUZE100_VREFDDRCON, 750000), - PFUZE100_VGEN_REG(VGEN1, PFUZE100_VGEN1VOL, 800000, 1550000, 50000), - PFUZE100_VGEN_REG(VGEN2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000), - PFUZE100_VGEN_REG(VGEN3, PFUZE100_VGEN3VOL, 1800000, 3300000, 100000), - PFUZE100_VGEN_REG(VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000), - PFUZE100_VGEN_REG(VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), - PFUZE100_VGEN_REG(VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), + PFUZE100_SW_REG(PFUZE100, SW1AB, PFUZE100_SW1ABVOL, 300000, 1875000, 25000), + PFUZE100_SW_REG(PFUZE100, SW1C, PFUZE100_SW1CVOL, 300000, 1875000, 25000), + PFUZE100_SW_REG(PFUZE100, SW2, PFUZE100_SW2VOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(PFUZE100, SW3A, PFUZE100_SW3AVOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(PFUZE100, SW3B, PFUZE100_SW3BVOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(PFUZE100, SW4, PFUZE100_SW4VOL, 400000, 1975000, 25000), + PFUZE100_SWB_REG(PFUZE100, SWBST, PFUZE100_SWBSTCON1, 0x3 , pfuze100_swbst), + PFUZE100_SWB_REG(PFUZE100, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), + PFUZE100_FIXED_REG(PFUZE100, VREFDDR, PFUZE100_VREFDDRCON, 750000), + PFUZE100_VGEN_REG(PFUZE100, VGEN1, PFUZE100_VGEN1VOL, 800000, 1550000, 50000), + PFUZE100_VGEN_REG(PFUZE100, VGEN2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000), + PFUZE100_VGEN_REG(PFUZE100, VGEN3, PFUZE100_VGEN3VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE100, VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE100, VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE100, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), +}; + +static struct pfuze_regulator pfuze200_regulators[] = { + PFUZE100_SW_REG(PFUZE200, SW1AB, PFUZE100_SW1ABVOL, 300000, 1875000, 25000), + PFUZE100_SW_REG(PFUZE200, SW2, PFUZE100_SW2VOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(PFUZE200, SW3A, PFUZE100_SW3AVOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(PFUZE200, SW3B, PFUZE100_SW3BVOL, 400000, 1975000, 25000), + PFUZE100_SWB_REG(PFUZE200, SWBST, PFUZE100_SWBSTCON1, 0x3 , pfuze100_swbst), + PFUZE100_SWB_REG(PFUZE200, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), + PFUZE100_FIXED_REG(PFUZE200, VREFDDR, PFUZE100_VREFDDRCON, 750000), + PFUZE100_VGEN_REG(PFUZE200, VGEN1, PFUZE100_VGEN1VOL, 800000, 1550000, 50000), + PFUZE100_VGEN_REG(PFUZE200, VGEN2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000), + PFUZE100_VGEN_REG(PFUZE200, VGEN3, PFUZE100_VGEN3VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE200, VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE200, VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE200, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), }; +static struct pfuze_regulator *pfuze_regulators; + #ifdef CONFIG_OF +/* PFUZE100 */ static struct of_regulator_match pfuze100_matches[] = { { .name = "sw1ab", }, { .name = "sw1c", }, @@ -244,24 +269,56 @@ static struct of_regulator_match pfuze100_matches[] = { { .name = "vgen6", }, }; +/* PFUZE200 */ +static struct of_regulator_match pfuze200_matches[] = { + + { .name = "sw1ab", }, + { .name = "sw2", }, + { .name = "sw3a", }, + { .name = "sw3b", }, + { .name = "swbst", }, + { .name = "vsnvs", }, + { .name = "vrefddr", }, + { .name = "vgen1", }, + { .name = "vgen2", }, + { .name = "vgen3", }, + { .name = "vgen4", }, + { .name = "vgen5", }, + { .name = "vgen6", }, +}; + +static struct of_regulator_match *pfuze_matches; + static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) { struct device *dev = chip->dev; struct device_node *np, *parent; int ret; - np = of_node_get(dev->parent->of_node); + np = of_node_get(dev->of_node); if (!np) - return 0; + return -EINVAL; - parent = of_find_node_by_name(np, "regulators"); + parent = of_get_child_by_name(np, "regulators"); if (!parent) { dev_err(dev, "regulators node not found\n"); return -EINVAL; } - ret = of_regulator_match(dev, parent, pfuze100_matches, - ARRAY_SIZE(pfuze100_matches)); + switch (chip->chip_id) { + case PFUZE200: + pfuze_matches = pfuze200_matches; + ret = of_regulator_match(dev, parent, pfuze200_matches, + ARRAY_SIZE(pfuze200_matches)); + break; + + case PFUZE100: + default: + pfuze_matches = pfuze100_matches; + ret = of_regulator_match(dev, parent, pfuze100_matches, + ARRAY_SIZE(pfuze100_matches)); + break; + } of_node_put(parent); if (ret < 0) { @@ -275,12 +332,12 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) static inline struct regulator_init_data *match_init_data(int index) { - return pfuze100_matches[index].init_data; + return pfuze_matches[index].init_data; } static inline struct device_node *match_of_node(int index) { - return pfuze100_matches[index].of_node; + return pfuze_matches[index].of_node; } #else static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) @@ -308,16 +365,14 @@ static int pfuze_identify(struct pfuze_chip *pfuze_chip) if (ret) return ret; - switch (value & 0x0f) { - /* - * Freescale misprogrammed 1-3% of parts prior to week 8 of 2013 - * as ID=8 - */ - case 0x8: + if (((value & 0x0f) == 0x8) && (pfuze_chip->chip_id == PFUZE100)) { + /* + * Freescale misprogrammed 1-3% of parts prior to week 8 of 2013 + * as ID=8 in PFUZE100 + */ dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8"); - case 0x0: - break; - default: + } else if ((value & 0x0f) != pfuze_chip->chip_id) { + /* device id NOT match with your setting */ dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value); return -ENODEV; } @@ -353,17 +408,31 @@ static int pfuze100_regulator_probe(struct i2c_client *client, dev_get_platdata(&client->dev); struct regulator_config config = { }; int i, ret; + const struct of_device_id *match; + u32 regulator_num; + u32 sw_check_start, sw_check_end; pfuze_chip = devm_kzalloc(&client->dev, sizeof(*pfuze_chip), GFP_KERNEL); if (!pfuze_chip) return -ENOMEM; - i2c_set_clientdata(client, pfuze_chip); - - memcpy(pfuze_chip->regulator_descs, pfuze100_regulators, - sizeof(pfuze_chip->regulator_descs)); + if (client->dev.of_node) { + match = of_match_device(of_match_ptr(pfuze_dt_ids), + &client->dev); + if (!match) { + dev_err(&client->dev, "Error: No device match found\n"); + return -ENODEV; + } + pfuze_chip->chip_id = (int)(long)match->data; + } else if (id) { + pfuze_chip->chip_id = id->driver_data; + } else { + dev_err(&client->dev, "No dts match or id table match found\n"); + return -ENODEV; + } + i2c_set_clientdata(client, pfuze_chip); pfuze_chip->dev = &client->dev; pfuze_chip->regmap = devm_regmap_init_i2c(client, &pfuze_regmap_config); @@ -380,11 +449,34 @@ static int pfuze100_regulator_probe(struct i2c_client *client, return ret; } + /* use the right regulators after identify the right device */ + switch (pfuze_chip->chip_id) { + 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; + regulator_num = ARRAY_SIZE(pfuze100_regulators); + sw_check_start = PFUZE100_SW2; + sw_check_end = PFUZE100_SW4; + break; + } + dev_info(&client->dev, "pfuze%s found.\n", + (pfuze_chip->chip_id == PFUZE100) ? "100" : "200"); + + memcpy(pfuze_chip->regulator_descs, pfuze_regulators, + sizeof(pfuze_chip->regulator_descs)); + ret = pfuze_parse_regulators_dt(pfuze_chip); if (ret) return ret; - for (i = 0; i < PFUZE100_MAX_REGULATOR; i++) { + for (i = 0; i < regulator_num; i++) { struct regulator_init_data *init_data; struct regulator_desc *desc; int val; @@ -397,7 +489,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client, init_data = match_init_data(i); /* SW2~SW4 high bit check and modify the voltage value table */ - if (i > PFUZE100_SW1C && i < PFUZE100_SWBST) { + 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; @@ -415,7 +507,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client, devm_regulator_register(&client->dev, desc, &config); if (IS_ERR(pfuze_chip->regulators[i])) { dev_err(&client->dev, "register regulator%s failed\n", - pfuze100_regulators[i].desc.name); + pfuze_regulators[i].desc.name); return PTR_ERR(pfuze_chip->regulators[i]); } } @@ -435,6 +527,6 @@ static struct i2c_driver pfuze_driver = { module_i2c_driver(pfuze_driver); MODULE_AUTHOR("Robin Gong <b38343@freescale.com>"); -MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100 PMIC"); +MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/PFUZE200 PMIC"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("i2c:pfuze100-regulator"); diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c index b58affb33143..4c414ae109ae 100644 --- a/drivers/regulator/rc5t583-regulator.c +++ b/drivers/regulator/rc5t583-regulator.c @@ -119,7 +119,6 @@ static int rc5t583_regulator_probe(struct platform_device *pdev) { struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent); struct rc5t583_platform_data *pdata = dev_get_platdata(rc5t583->dev); - struct regulator_init_data *reg_data; struct regulator_config config = { }; struct rc5t583_regulator *reg = NULL; struct rc5t583_regulator *regs; @@ -135,19 +134,11 @@ static int rc5t583_regulator_probe(struct platform_device *pdev) regs = devm_kzalloc(&pdev->dev, RC5T583_REGULATOR_MAX * sizeof(struct rc5t583_regulator), GFP_KERNEL); - if (!regs) { - dev_err(&pdev->dev, "Memory allocation failed exiting..\n"); + if (!regs) return -ENOMEM; - } for (id = 0; id < RC5T583_REGULATOR_MAX; ++id) { - reg_data = pdata->reg_init_data[id]; - - /* No need to register if there is no regulator data */ - if (!reg_data) - continue; - reg = ®s[id]; ri = &rc5t583_reg_info[id]; reg->reg_info = ri; @@ -169,7 +160,7 @@ static int rc5t583_regulator_probe(struct platform_device *pdev) skip_ext_pwr_config: config.dev = &pdev->dev; - config.init_data = reg_data; + config.init_data = pdata->reg_init_data[id]; config.driver_data = reg; config.regmap = rc5t583->regmap; diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c new file mode 100644 index 000000000000..808b3aa7a42c --- /dev/null +++ b/drivers/regulator/s2mpa01.c @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd + * http://www.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/bug.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/mfd/samsung/core.h> +#include <linux/mfd/samsung/s2mpa01.h> + +#define S2MPA01_REGULATOR_CNT ARRAY_SIZE(regulators) + +struct s2mpa01_info { + int ramp_delay24; + int ramp_delay3; + int ramp_delay5; + int ramp_delay16; + int ramp_delay7; + int ramp_delay8910; +}; + +static int get_ramp_delay(int ramp_delay) +{ + unsigned char cnt = 0; + + ramp_delay /= 6250; + + while (true) { + ramp_delay = ramp_delay >> 1; + if (ramp_delay == 0) + break; + cnt++; + } + + if (cnt > 3) + cnt = 3; + + return cnt; +} + +static int s2mpa01_regulator_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + struct s2mpa01_info *s2mpa01 = rdev_get_drvdata(rdev); + unsigned int ramp_delay = 0; + int old_volt, new_volt; + + switch (rdev->desc->id) { + case S2MPA01_BUCK2: + case S2MPA01_BUCK4: + ramp_delay = s2mpa01->ramp_delay24; + break; + case S2MPA01_BUCK3: + ramp_delay = s2mpa01->ramp_delay3; + break; + case S2MPA01_BUCK5: + ramp_delay = s2mpa01->ramp_delay5; + break; + case S2MPA01_BUCK1: + case S2MPA01_BUCK6: + ramp_delay = s2mpa01->ramp_delay16; + break; + case S2MPA01_BUCK7: + ramp_delay = s2mpa01->ramp_delay7; + break; + case S2MPA01_BUCK8: + case S2MPA01_BUCK9: + case S2MPA01_BUCK10: + ramp_delay = s2mpa01->ramp_delay8910; + break; + } + + if (ramp_delay == 0) + ramp_delay = rdev->desc->ramp_delay; + + old_volt = rdev->desc->min_uV + (rdev->desc->uV_step * old_selector); + new_volt = rdev->desc->min_uV + (rdev->desc->uV_step * new_selector); + + return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay); +} + +static int s2mpa01_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + struct s2mpa01_info *s2mpa01 = rdev_get_drvdata(rdev); + unsigned int ramp_val, ramp_shift, ramp_reg = S2MPA01_REG_RAMP2; + unsigned int ramp_enable = 1, enable_shift = 0; + int ret; + + switch (rdev->desc->id) { + case S2MPA01_BUCK1: + enable_shift = S2MPA01_BUCK1_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + if (ramp_delay > s2mpa01->ramp_delay16) + s2mpa01->ramp_delay16 = ramp_delay; + else + ramp_delay = s2mpa01->ramp_delay16; + + ramp_shift = S2MPA01_BUCK16_RAMP_SHIFT; + ramp_reg = S2MPA01_REG_RAMP1; + break; + case S2MPA01_BUCK2: + enable_shift = S2MPA01_BUCK2_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + if (ramp_delay > s2mpa01->ramp_delay24) + s2mpa01->ramp_delay24 = ramp_delay; + else + ramp_delay = s2mpa01->ramp_delay24; + + ramp_shift = S2MPA01_BUCK24_RAMP_SHIFT; + ramp_reg = S2MPA01_REG_RAMP1; + break; + case S2MPA01_BUCK3: + enable_shift = S2MPA01_BUCK3_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + s2mpa01->ramp_delay3 = ramp_delay; + ramp_shift = S2MPA01_BUCK3_RAMP_SHIFT; + ramp_reg = S2MPA01_REG_RAMP1; + break; + case S2MPA01_BUCK4: + enable_shift = S2MPA01_BUCK4_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + if (ramp_delay > s2mpa01->ramp_delay24) + s2mpa01->ramp_delay24 = ramp_delay; + else + ramp_delay = s2mpa01->ramp_delay24; + + ramp_shift = S2MPA01_BUCK24_RAMP_SHIFT; + ramp_reg = S2MPA01_REG_RAMP1; + break; + case S2MPA01_BUCK5: + s2mpa01->ramp_delay5 = ramp_delay; + ramp_shift = S2MPA01_BUCK5_RAMP_SHIFT; + break; + case S2MPA01_BUCK6: + if (ramp_delay > s2mpa01->ramp_delay16) + s2mpa01->ramp_delay16 = ramp_delay; + else + ramp_delay = s2mpa01->ramp_delay16; + + ramp_shift = S2MPA01_BUCK16_RAMP_SHIFT; + break; + case S2MPA01_BUCK7: + s2mpa01->ramp_delay7 = ramp_delay; + ramp_shift = S2MPA01_BUCK7_RAMP_SHIFT; + break; + case S2MPA01_BUCK8: + case S2MPA01_BUCK9: + case S2MPA01_BUCK10: + if (ramp_delay > s2mpa01->ramp_delay8910) + s2mpa01->ramp_delay8910 = ramp_delay; + else + ramp_delay = s2mpa01->ramp_delay8910; + + ramp_shift = S2MPA01_BUCK8910_RAMP_SHIFT; + break; + default: + return 0; + } + + if (!ramp_enable) + goto ramp_disable; + + if (enable_shift) { + ret = regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1, + 1 << enable_shift, 1 << enable_shift); + if (ret) { + dev_err(&rdev->dev, "failed to enable ramp rate\n"); + return ret; + } + } + + ramp_val = get_ramp_delay(ramp_delay); + + return regmap_update_bits(rdev->regmap, ramp_reg, 0x3 << ramp_shift, + ramp_val << ramp_shift); + +ramp_disable: + return regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1, + 1 << enable_shift, 0); +} + +static struct regulator_ops s2mpa01_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +static struct regulator_ops s2mpa01_buck_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = s2mpa01_regulator_set_voltage_time_sel, + .set_ramp_delay = s2mpa01_set_ramp_delay, +}; + +#define regulator_desc_ldo1(num) { \ + .name = "LDO"#num, \ + .id = S2MPA01_LDO##num, \ + .ops = &s2mpa01_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPA01_LDO_MIN, \ + .uV_step = S2MPA01_LDO_STEP1, \ + .n_voltages = S2MPA01_LDO_N_VOLTAGES, \ + .vsel_reg = S2MPA01_REG_L1CTRL + num - 1, \ + .vsel_mask = S2MPA01_LDO_VSEL_MASK, \ + .enable_reg = S2MPA01_REG_L1CTRL + num - 1, \ + .enable_mask = S2MPA01_ENABLE_MASK \ +} +#define regulator_desc_ldo2(num) { \ + .name = "LDO"#num, \ + .id = S2MPA01_LDO##num, \ + .ops = &s2mpa01_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPA01_LDO_MIN, \ + .uV_step = S2MPA01_LDO_STEP2, \ + .n_voltages = S2MPA01_LDO_N_VOLTAGES, \ + .vsel_reg = S2MPA01_REG_L1CTRL + num - 1, \ + .vsel_mask = S2MPA01_LDO_VSEL_MASK, \ + .enable_reg = S2MPA01_REG_L1CTRL + num - 1, \ + .enable_mask = S2MPA01_ENABLE_MASK \ +} + +#define regulator_desc_buck1_4(num) { \ + .name = "BUCK"#num, \ + .id = S2MPA01_BUCK##num, \ + .ops = &s2mpa01_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPA01_BUCK_MIN1, \ + .uV_step = S2MPA01_BUCK_STEP1, \ + .n_voltages = S2MPA01_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPA01_RAMP_DELAY, \ + .vsel_reg = S2MPA01_REG_B1CTRL2 + (num - 1) * 2, \ + .vsel_mask = S2MPA01_BUCK_VSEL_MASK, \ + .enable_reg = S2MPA01_REG_B1CTRL1 + (num - 1) * 2, \ + .enable_mask = S2MPA01_ENABLE_MASK \ +} + +#define regulator_desc_buck5 { \ + .name = "BUCK5", \ + .id = S2MPA01_BUCK5, \ + .ops = &s2mpa01_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPA01_BUCK_MIN2, \ + .uV_step = S2MPA01_BUCK_STEP1, \ + .n_voltages = S2MPA01_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPA01_RAMP_DELAY, \ + .vsel_reg = S2MPA01_REG_B5CTRL2, \ + .vsel_mask = S2MPA01_BUCK_VSEL_MASK, \ + .enable_reg = S2MPA01_REG_B5CTRL1, \ + .enable_mask = S2MPA01_ENABLE_MASK \ +} + +#define regulator_desc_buck6_7(num) { \ + .name = "BUCK"#num, \ + .id = S2MPA01_BUCK##num, \ + .ops = &s2mpa01_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPA01_BUCK_MIN1, \ + .uV_step = S2MPA01_BUCK_STEP1, \ + .n_voltages = S2MPA01_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPA01_RAMP_DELAY, \ + .vsel_reg = S2MPA01_REG_B6CTRL2 + (num - 6) * 2, \ + .vsel_mask = S2MPA01_BUCK_VSEL_MASK, \ + .enable_reg = S2MPA01_REG_B6CTRL1 + (num - 6) * 2, \ + .enable_mask = S2MPA01_ENABLE_MASK \ +} + +#define regulator_desc_buck8 { \ + .name = "BUCK8", \ + .id = S2MPA01_BUCK8, \ + .ops = &s2mpa01_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPA01_BUCK_MIN2, \ + .uV_step = S2MPA01_BUCK_STEP2, \ + .n_voltages = S2MPA01_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPA01_RAMP_DELAY, \ + .vsel_reg = S2MPA01_REG_B8CTRL2, \ + .vsel_mask = S2MPA01_BUCK_VSEL_MASK, \ + .enable_reg = S2MPA01_REG_B8CTRL1, \ + .enable_mask = S2MPA01_ENABLE_MASK \ +} + +#define regulator_desc_buck9 { \ + .name = "BUCK9", \ + .id = S2MPA01_BUCK9, \ + .ops = &s2mpa01_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPA01_BUCK_MIN4, \ + .uV_step = S2MPA01_BUCK_STEP2, \ + .n_voltages = S2MPA01_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPA01_RAMP_DELAY, \ + .vsel_reg = S2MPA01_REG_B9CTRL2, \ + .vsel_mask = S2MPA01_BUCK_VSEL_MASK, \ + .enable_reg = S2MPA01_REG_B9CTRL1, \ + .enable_mask = S2MPA01_ENABLE_MASK \ +} + +#define regulator_desc_buck10 { \ + .name = "BUCK10", \ + .id = S2MPA01_BUCK10, \ + .ops = &s2mpa01_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPA01_BUCK_MIN3, \ + .uV_step = S2MPA01_BUCK_STEP2, \ + .n_voltages = S2MPA01_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPA01_RAMP_DELAY, \ + .vsel_reg = S2MPA01_REG_B10CTRL2, \ + .vsel_mask = S2MPA01_BUCK_VSEL_MASK, \ + .enable_reg = S2MPA01_REG_B10CTRL1, \ + .enable_mask = S2MPA01_ENABLE_MASK \ +} + +static struct regulator_desc regulators[] = { + regulator_desc_ldo2(1), + regulator_desc_ldo1(2), + regulator_desc_ldo1(3), + regulator_desc_ldo1(4), + regulator_desc_ldo1(5), + regulator_desc_ldo2(6), + regulator_desc_ldo1(7), + regulator_desc_ldo1(8), + regulator_desc_ldo1(9), + regulator_desc_ldo1(10), + regulator_desc_ldo2(11), + regulator_desc_ldo1(12), + regulator_desc_ldo1(13), + regulator_desc_ldo1(14), + regulator_desc_ldo1(15), + regulator_desc_ldo1(16), + regulator_desc_ldo1(17), + regulator_desc_ldo1(18), + regulator_desc_ldo1(19), + regulator_desc_ldo1(20), + regulator_desc_ldo1(21), + regulator_desc_ldo2(22), + regulator_desc_ldo2(23), + regulator_desc_ldo1(24), + regulator_desc_ldo1(25), + regulator_desc_ldo1(26), + regulator_desc_buck1_4(1), + regulator_desc_buck1_4(2), + regulator_desc_buck1_4(3), + regulator_desc_buck1_4(4), + regulator_desc_buck5, + regulator_desc_buck6_7(6), + regulator_desc_buck6_7(7), + regulator_desc_buck8, + regulator_desc_buck9, + regulator_desc_buck10, +}; + +static int s2mpa01_pmic_probe(struct platform_device *pdev) +{ + struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct sec_platform_data *pdata = dev_get_platdata(iodev->dev); + struct of_regulator_match rdata[S2MPA01_REGULATOR_MAX]; + struct device_node *reg_np = NULL; + struct regulator_config config = { }; + struct s2mpa01_info *s2mpa01; + int i; + + s2mpa01 = devm_kzalloc(&pdev->dev, sizeof(*s2mpa01), GFP_KERNEL); + if (!s2mpa01) + return -ENOMEM; + + for (i = 0; i < S2MPA01_REGULATOR_CNT; i++) + rdata[i].name = regulators[i].name; + + if (iodev->dev->of_node) { + reg_np = of_get_child_by_name(iodev->dev->of_node, + "regulators"); + if (!reg_np) { + dev_err(&pdev->dev, + "could not find regulators sub-node\n"); + return -EINVAL; + } + + of_regulator_match(&pdev->dev, reg_np, rdata, + S2MPA01_REGULATOR_MAX); + of_node_put(reg_np); + } + + platform_set_drvdata(pdev, s2mpa01); + + config.dev = &pdev->dev; + config.regmap = iodev->regmap_pmic; + config.driver_data = s2mpa01; + + for (i = 0; i < S2MPA01_REGULATOR_MAX; i++) { + struct regulator_dev *rdev; + if (pdata) + config.init_data = pdata->regulators[i].initdata; + else + config.init_data = rdata[i].init_data; + + if (reg_np) + config.of_node = rdata[i].of_node; + + rdev = devm_regulator_register(&pdev->dev, + ®ulators[i], &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "regulator init failed for %d\n", + i); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct platform_device_id s2mpa01_pmic_id[] = { + { "s2mpa01-pmic", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, s2mpa01_pmic_id); + +static struct platform_driver s2mpa01_pmic_driver = { + .driver = { + .name = "s2mpa01-pmic", + .owner = THIS_MODULE, + }, + .probe = s2mpa01_pmic_probe, + .id_table = s2mpa01_pmic_id, +}; + +module_platform_driver(s2mpa01_pmic_driver); + +/* Module information */ +MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); +MODULE_AUTHOR("Sachin Kamat <sachin.kamat@samsung.com>"); +MODULE_DESCRIPTION("SAMSUNG S2MPA01 Regulator Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index cd0b9e35a56d..68fd54702edb 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -1,13 +1,18 @@ /* * s2mps11.c * - * Copyright (c) 2012 Samsung Electronics Co., Ltd + * Copyright (c) 2012-2014 Samsung Electronics Co., Ltd * http://www.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 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. * */ @@ -24,18 +29,21 @@ #include <linux/regulator/of_regulator.h> #include <linux/mfd/samsung/core.h> #include <linux/mfd/samsung/s2mps11.h> - -#define S2MPS11_REGULATOR_CNT ARRAY_SIZE(regulators) +#include <linux/mfd/samsung/s2mps14.h> struct s2mps11_info { - struct regulator_dev *rdev[S2MPS11_REGULATOR_MAX]; - + unsigned int rdev_num; int ramp_delay2; int ramp_delay34; int ramp_delay5; int ramp_delay16; int ramp_delay7810; int ramp_delay9; + /* + * One bit for each S2MPS14 regulator whether the suspend mode + * was enabled. + */ + unsigned int s2mps14_suspend_state:30; }; static int get_ramp_delay(int ramp_delay) @@ -65,7 +73,7 @@ static int s2mps11_regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int ramp_delay = 0; int old_volt, new_volt; - switch (rdev->desc->id) { + switch (rdev_get_id(rdev)) { case S2MPS11_BUCK2: ramp_delay = s2mps11->ramp_delay2; break; @@ -105,7 +113,7 @@ static int s2mps11_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) unsigned int ramp_enable = 1, enable_shift = 0; int ret; - switch (rdev->desc->id) { + switch (rdev_get_id(rdev)) { case S2MPS11_BUCK1: if (ramp_delay > s2mps11->ramp_delay16) s2mps11->ramp_delay16 = ramp_delay; @@ -236,7 +244,7 @@ static struct regulator_ops s2mps11_buck_ops = { .set_ramp_delay = s2mps11_set_ramp_delay, }; -#define regulator_desc_ldo1(num) { \ +#define regulator_desc_s2mps11_ldo1(num) { \ .name = "LDO"#num, \ .id = S2MPS11_LDO##num, \ .ops = &s2mps11_ldo_ops, \ @@ -250,7 +258,7 @@ static struct regulator_ops s2mps11_buck_ops = { .enable_reg = S2MPS11_REG_L1CTRL + num - 1, \ .enable_mask = S2MPS11_ENABLE_MASK \ } -#define regulator_desc_ldo2(num) { \ +#define regulator_desc_s2mps11_ldo2(num) { \ .name = "LDO"#num, \ .id = S2MPS11_LDO##num, \ .ops = &s2mps11_ldo_ops, \ @@ -265,7 +273,7 @@ static struct regulator_ops s2mps11_buck_ops = { .enable_mask = S2MPS11_ENABLE_MASK \ } -#define regulator_desc_buck1_4(num) { \ +#define regulator_desc_s2mps11_buck1_4(num) { \ .name = "BUCK"#num, \ .id = S2MPS11_BUCK##num, \ .ops = &s2mps11_buck_ops, \ @@ -281,7 +289,7 @@ static struct regulator_ops s2mps11_buck_ops = { .enable_mask = S2MPS11_ENABLE_MASK \ } -#define regulator_desc_buck5 { \ +#define regulator_desc_s2mps11_buck5 { \ .name = "BUCK5", \ .id = S2MPS11_BUCK5, \ .ops = &s2mps11_buck_ops, \ @@ -297,7 +305,7 @@ static struct regulator_ops s2mps11_buck_ops = { .enable_mask = S2MPS11_ENABLE_MASK \ } -#define regulator_desc_buck6_8(num) { \ +#define regulator_desc_s2mps11_buck6_8(num) { \ .name = "BUCK"#num, \ .id = S2MPS11_BUCK##num, \ .ops = &s2mps11_buck_ops, \ @@ -313,7 +321,7 @@ static struct regulator_ops s2mps11_buck_ops = { .enable_mask = S2MPS11_ENABLE_MASK \ } -#define regulator_desc_buck9 { \ +#define regulator_desc_s2mps11_buck9 { \ .name = "BUCK9", \ .id = S2MPS11_BUCK9, \ .ops = &s2mps11_buck_ops, \ @@ -329,7 +337,7 @@ static struct regulator_ops s2mps11_buck_ops = { .enable_mask = S2MPS11_ENABLE_MASK \ } -#define regulator_desc_buck10 { \ +#define regulator_desc_s2mps11_buck10 { \ .name = "BUCK10", \ .id = S2MPS11_BUCK10, \ .ops = &s2mps11_buck_ops, \ @@ -345,72 +353,252 @@ static struct regulator_ops s2mps11_buck_ops = { .enable_mask = S2MPS11_ENABLE_MASK \ } -static struct regulator_desc regulators[] = { - regulator_desc_ldo2(1), - regulator_desc_ldo1(2), - regulator_desc_ldo1(3), - regulator_desc_ldo1(4), - regulator_desc_ldo1(5), - regulator_desc_ldo2(6), - regulator_desc_ldo1(7), - regulator_desc_ldo1(8), - regulator_desc_ldo1(9), - regulator_desc_ldo1(10), - regulator_desc_ldo2(11), - regulator_desc_ldo1(12), - regulator_desc_ldo1(13), - regulator_desc_ldo1(14), - regulator_desc_ldo1(15), - regulator_desc_ldo1(16), - regulator_desc_ldo1(17), - regulator_desc_ldo1(18), - regulator_desc_ldo1(19), - regulator_desc_ldo1(20), - regulator_desc_ldo1(21), - regulator_desc_ldo2(22), - regulator_desc_ldo2(23), - regulator_desc_ldo1(24), - regulator_desc_ldo1(25), - regulator_desc_ldo1(26), - regulator_desc_ldo2(27), - regulator_desc_ldo1(28), - regulator_desc_ldo1(29), - regulator_desc_ldo1(30), - regulator_desc_ldo1(31), - regulator_desc_ldo1(32), - regulator_desc_ldo1(33), - regulator_desc_ldo1(34), - regulator_desc_ldo1(35), - regulator_desc_ldo1(36), - regulator_desc_ldo1(37), - regulator_desc_ldo1(38), - regulator_desc_buck1_4(1), - regulator_desc_buck1_4(2), - regulator_desc_buck1_4(3), - regulator_desc_buck1_4(4), - regulator_desc_buck5, - regulator_desc_buck6_8(6), - regulator_desc_buck6_8(7), - regulator_desc_buck6_8(8), - regulator_desc_buck9, - regulator_desc_buck10, +static const struct regulator_desc s2mps11_regulators[] = { + regulator_desc_s2mps11_ldo2(1), + regulator_desc_s2mps11_ldo1(2), + regulator_desc_s2mps11_ldo1(3), + regulator_desc_s2mps11_ldo1(4), + regulator_desc_s2mps11_ldo1(5), + regulator_desc_s2mps11_ldo2(6), + regulator_desc_s2mps11_ldo1(7), + regulator_desc_s2mps11_ldo1(8), + regulator_desc_s2mps11_ldo1(9), + regulator_desc_s2mps11_ldo1(10), + regulator_desc_s2mps11_ldo2(11), + regulator_desc_s2mps11_ldo1(12), + regulator_desc_s2mps11_ldo1(13), + regulator_desc_s2mps11_ldo1(14), + regulator_desc_s2mps11_ldo1(15), + regulator_desc_s2mps11_ldo1(16), + regulator_desc_s2mps11_ldo1(17), + regulator_desc_s2mps11_ldo1(18), + regulator_desc_s2mps11_ldo1(19), + regulator_desc_s2mps11_ldo1(20), + regulator_desc_s2mps11_ldo1(21), + regulator_desc_s2mps11_ldo2(22), + regulator_desc_s2mps11_ldo2(23), + regulator_desc_s2mps11_ldo1(24), + regulator_desc_s2mps11_ldo1(25), + regulator_desc_s2mps11_ldo1(26), + regulator_desc_s2mps11_ldo2(27), + regulator_desc_s2mps11_ldo1(28), + regulator_desc_s2mps11_ldo1(29), + regulator_desc_s2mps11_ldo1(30), + regulator_desc_s2mps11_ldo1(31), + regulator_desc_s2mps11_ldo1(32), + regulator_desc_s2mps11_ldo1(33), + regulator_desc_s2mps11_ldo1(34), + regulator_desc_s2mps11_ldo1(35), + regulator_desc_s2mps11_ldo1(36), + regulator_desc_s2mps11_ldo1(37), + regulator_desc_s2mps11_ldo1(38), + regulator_desc_s2mps11_buck1_4(1), + regulator_desc_s2mps11_buck1_4(2), + regulator_desc_s2mps11_buck1_4(3), + regulator_desc_s2mps11_buck1_4(4), + regulator_desc_s2mps11_buck5, + regulator_desc_s2mps11_buck6_8(6), + regulator_desc_s2mps11_buck6_8(7), + regulator_desc_s2mps11_buck6_8(8), + regulator_desc_s2mps11_buck9, + regulator_desc_s2mps11_buck10, +}; + +static int s2mps14_regulator_enable(struct regulator_dev *rdev) +{ + struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); + unsigned int val; + + if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev))) + val = S2MPS14_ENABLE_SUSPEND; + else + val = rdev->desc->enable_mask; + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); +} + +static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev) +{ + int ret; + unsigned int val; + struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); + + /* LDO3 should be always on and does not support suspend mode */ + if (rdev_get_id(rdev) == S2MPS14_LDO3) + return 0; + + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); + if (ret < 0) + return ret; + + s2mps11->s2mps14_suspend_state |= (1 << rdev_get_id(rdev)); + /* + * Don't enable suspend mode if regulator is already disabled because + * this would effectively for a short time turn on the regulator after + * resuming. + * However we still want to toggle the suspend_state bit for regulator + * in case if it got enabled before suspending the system. + */ + if (!(val & rdev->desc->enable_mask)) + return 0; + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, S2MPS14_ENABLE_SUSPEND); +} + +static struct regulator_ops s2mps14_reg_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = s2mps14_regulator_enable, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_suspend_disable = s2mps14_regulator_set_suspend_disable, +}; + +#define regulator_desc_s2mps14_ldo1(num) { \ + .name = "LDO"#num, \ + .id = S2MPS14_LDO##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS14_LDO_MIN_800MV, \ + .uV_step = S2MPS14_LDO_STEP_25MV, \ + .n_voltages = S2MPS14_LDO_N_VOLTAGES, \ + .vsel_reg = S2MPS14_REG_L1CTRL + num - 1, \ + .vsel_mask = S2MPS14_LDO_VSEL_MASK, \ + .enable_reg = S2MPS14_REG_L1CTRL + num - 1, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} +#define regulator_desc_s2mps14_ldo2(num) { \ + .name = "LDO"#num, \ + .id = S2MPS14_LDO##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS14_LDO_MIN_1800MV, \ + .uV_step = S2MPS14_LDO_STEP_25MV, \ + .n_voltages = S2MPS14_LDO_N_VOLTAGES, \ + .vsel_reg = S2MPS14_REG_L1CTRL + num - 1, \ + .vsel_mask = S2MPS14_LDO_VSEL_MASK, \ + .enable_reg = S2MPS14_REG_L1CTRL + num - 1, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} +#define regulator_desc_s2mps14_ldo3(num) { \ + .name = "LDO"#num, \ + .id = S2MPS14_LDO##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS14_LDO_MIN_800MV, \ + .uV_step = S2MPS14_LDO_STEP_12_5MV, \ + .n_voltages = S2MPS14_LDO_N_VOLTAGES, \ + .vsel_reg = S2MPS14_REG_L1CTRL + num - 1, \ + .vsel_mask = S2MPS14_LDO_VSEL_MASK, \ + .enable_reg = S2MPS14_REG_L1CTRL + num - 1, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} +#define regulator_desc_s2mps14_buck1235(num) { \ + .name = "BUCK"#num, \ + .id = S2MPS14_BUCK##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS14_BUCK1235_MIN_600MV, \ + .uV_step = S2MPS14_BUCK1235_STEP_6_25MV, \ + .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \ + .linear_min_sel = S2MPS14_BUCK1235_START_SEL, \ + .ramp_delay = S2MPS14_BUCK_RAMP_DELAY, \ + .vsel_reg = S2MPS14_REG_B1CTRL2 + (num - 1) * 2, \ + .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS14_REG_B1CTRL1 + (num - 1) * 2, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} +#define regulator_desc_s2mps14_buck4(num) { \ + .name = "BUCK"#num, \ + .id = S2MPS14_BUCK##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = S2MPS14_BUCK4_MIN_1400MV, \ + .uV_step = S2MPS14_BUCK4_STEP_12_5MV, \ + .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \ + .linear_min_sel = S2MPS14_BUCK4_START_SEL, \ + .ramp_delay = S2MPS14_BUCK_RAMP_DELAY, \ + .vsel_reg = S2MPS14_REG_B1CTRL2 + (num - 1) * 2, \ + .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS14_REG_B1CTRL1 + (num - 1) * 2, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} + +static const struct regulator_desc s2mps14_regulators[] = { + regulator_desc_s2mps14_ldo3(1), + regulator_desc_s2mps14_ldo3(2), + regulator_desc_s2mps14_ldo1(3), + regulator_desc_s2mps14_ldo1(4), + regulator_desc_s2mps14_ldo3(5), + regulator_desc_s2mps14_ldo3(6), + regulator_desc_s2mps14_ldo1(7), + regulator_desc_s2mps14_ldo2(8), + regulator_desc_s2mps14_ldo3(9), + regulator_desc_s2mps14_ldo3(10), + regulator_desc_s2mps14_ldo1(11), + regulator_desc_s2mps14_ldo2(12), + regulator_desc_s2mps14_ldo2(13), + regulator_desc_s2mps14_ldo2(14), + regulator_desc_s2mps14_ldo2(15), + regulator_desc_s2mps14_ldo2(16), + regulator_desc_s2mps14_ldo2(17), + regulator_desc_s2mps14_ldo2(18), + regulator_desc_s2mps14_ldo1(19), + regulator_desc_s2mps14_ldo1(20), + regulator_desc_s2mps14_ldo1(21), + regulator_desc_s2mps14_ldo3(22), + regulator_desc_s2mps14_ldo1(23), + regulator_desc_s2mps14_ldo2(24), + regulator_desc_s2mps14_ldo2(25), + regulator_desc_s2mps14_buck1235(1), + regulator_desc_s2mps14_buck1235(2), + regulator_desc_s2mps14_buck1235(3), + regulator_desc_s2mps14_buck4(4), + regulator_desc_s2mps14_buck1235(5), }; static int s2mps11_pmic_probe(struct platform_device *pdev) { struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct sec_platform_data *pdata = dev_get_platdata(iodev->dev); - struct of_regulator_match rdata[S2MPS11_REGULATOR_MAX]; + struct sec_platform_data *pdata = iodev->pdata; + struct of_regulator_match *rdata = NULL; struct device_node *reg_np = NULL; struct regulator_config config = { }; struct s2mps11_info *s2mps11; - int i, ret; + int i, ret = 0; + const struct regulator_desc *regulators; + enum sec_device_type dev_type; s2mps11 = devm_kzalloc(&pdev->dev, sizeof(struct s2mps11_info), GFP_KERNEL); if (!s2mps11) return -ENOMEM; + dev_type = platform_get_device_id(pdev)->driver_data; + switch (dev_type) { + case S2MPS11X: + s2mps11->rdev_num = ARRAY_SIZE(s2mps11_regulators); + regulators = s2mps11_regulators; + break; + case S2MPS14X: + s2mps11->rdev_num = ARRAY_SIZE(s2mps14_regulators); + regulators = s2mps14_regulators; + break; + default: + dev_err(&pdev->dev, "Invalid device type: %u\n", dev_type); + return -EINVAL; + }; + if (!iodev->dev->of_node) { if (pdata) { goto common_reg; @@ -421,16 +609,22 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) } } - for (i = 0; i < S2MPS11_REGULATOR_CNT; i++) + rdata = kzalloc(sizeof(*rdata) * s2mps11->rdev_num, GFP_KERNEL); + if (!rdata) + return -ENOMEM; + + for (i = 0; i < s2mps11->rdev_num; i++) rdata[i].name = regulators[i].name; - reg_np = of_find_node_by_name(iodev->dev->of_node, "regulators"); + reg_np = of_get_child_by_name(iodev->dev->of_node, "regulators"); if (!reg_np) { dev_err(&pdev->dev, "could not find regulators sub-node\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } - of_regulator_match(&pdev->dev, reg_np, rdata, S2MPS11_REGULATOR_MAX); + of_regulator_match(&pdev->dev, reg_np, rdata, s2mps11->rdev_num); + of_node_put(reg_np); common_reg: platform_set_drvdata(pdev, s2mps11); @@ -438,7 +632,9 @@ common_reg: config.dev = &pdev->dev; config.regmap = iodev->regmap_pmic; config.driver_data = s2mps11; - for (i = 0; i < S2MPS11_REGULATOR_MAX; i++) { + for (i = 0; i < s2mps11->rdev_num; i++) { + struct regulator_dev *regulator; + if (!reg_np) { config.init_data = pdata->regulators[i].initdata; config.of_node = pdata->regulators[i].reg_node; @@ -447,21 +643,25 @@ common_reg: config.of_node = rdata[i].of_node; } - s2mps11->rdev[i] = devm_regulator_register(&pdev->dev, + regulator = devm_regulator_register(&pdev->dev, ®ulators[i], &config); - if (IS_ERR(s2mps11->rdev[i])) { - ret = PTR_ERR(s2mps11->rdev[i]); + if (IS_ERR(regulator)) { + ret = PTR_ERR(regulator); dev_err(&pdev->dev, "regulator init failed for %d\n", i); - return ret; + goto out; } } - return 0; +out: + kfree(rdata); + + return ret; } static const struct platform_device_id s2mps11_pmic_id[] = { - { "s2mps11-pmic", 0}, + { "s2mps11-pmic", S2MPS11X}, + { "s2mps14-pmic", S2MPS14X}, { }, }; MODULE_DEVICE_TABLE(platform, s2mps11_pmic_id); @@ -489,5 +689,5 @@ module_exit(s2mps11_pmic_exit); /* Module information */ MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>"); -MODULE_DESCRIPTION("SAMSUNG S2MPS11 Regulator Driver"); +MODULE_DESCRIPTION("SAMSUNG S2MPS11/S2MPS14 Regulator Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index d958dfa05125..f05badabd69e 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -11,11 +11,8 @@ * */ -#include <linux/bug.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/of_gpio.h> -#include <linux/slab.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> @@ -170,12 +167,11 @@ static unsigned int s5m8767_opmode_reg[][4] = { {0x0, 0x3, 0x1, 0x1}, /* BUCK9 */ }; -static int s5m8767_get_register(struct regulator_dev *rdev, int *reg, - int *enable_ctrl) +static int s5m8767_get_register(struct s5m8767_info *s5m8767, int reg_id, + int *reg, int *enable_ctrl) { - int i, reg_id = rdev_get_id(rdev); + int i; unsigned int mode; - struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); switch (reg_id) { case S5M8767_LDO1 ... S5M8767_LDO2: @@ -214,53 +210,6 @@ static int s5m8767_get_register(struct regulator_dev *rdev, int *reg, return 0; } -static int s5m8767_reg_is_enabled(struct regulator_dev *rdev) -{ - struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); - int ret, reg; - int enable_ctrl; - unsigned int val; - - ret = s5m8767_get_register(rdev, ®, &enable_ctrl); - if (ret == -EINVAL) - return 1; - else if (ret) - return ret; - - ret = regmap_read(s5m8767->iodev->regmap_pmic, reg, &val); - if (ret) - return ret; - - return (val & S5M8767_ENCTRL_MASK) == enable_ctrl; -} - -static int s5m8767_reg_enable(struct regulator_dev *rdev) -{ - struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); - int ret, reg; - int enable_ctrl; - - ret = s5m8767_get_register(rdev, ®, &enable_ctrl); - if (ret) - return ret; - - return regmap_update_bits(s5m8767->iodev->regmap_pmic, reg, - S5M8767_ENCTRL_MASK, enable_ctrl); -} - -static int s5m8767_reg_disable(struct regulator_dev *rdev) -{ - struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); - int ret, reg, enable_ctrl; - - ret = s5m8767_get_register(rdev, ®, &enable_ctrl); - if (ret) - return ret; - - return regmap_update_bits(s5m8767->iodev->regmap_pmic, reg, - S5M8767_ENCTRL_MASK, ~S5M8767_ENCTRL_MASK); -} - static int s5m8767_get_vsel_reg(int reg_id, struct s5m8767_info *s5m8767) { int reg; @@ -410,9 +359,9 @@ static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev, static struct regulator_ops s5m8767_ops = { .list_voltage = regulator_list_voltage_linear, - .is_enabled = s5m8767_reg_is_enabled, - .enable = s5m8767_reg_enable, - .disable = s5m8767_reg_disable, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = s5m8767_set_voltage_sel, .set_voltage_time_sel = s5m8767_set_voltage_time_sel, @@ -420,9 +369,9 @@ static struct regulator_ops s5m8767_ops = { static struct regulator_ops s5m8767_buck78_ops = { .list_voltage = regulator_list_voltage_linear, - .is_enabled = s5m8767_reg_is_enabled, - .enable = s5m8767_reg_enable, - .disable = s5m8767_reg_disable, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, }; @@ -483,6 +432,66 @@ static struct regulator_desc regulators[] = { s5m8767_regulator_desc(BUCK9), }; +/* + * Enable GPIO control over BUCK9 in regulator_config for that regulator. + */ +static void s5m8767_regulator_config_ext_control(struct s5m8767_info *s5m8767, + struct sec_regulator_data *rdata, + struct regulator_config *config) +{ + int i, mode = 0; + + if (rdata->id != S5M8767_BUCK9) + return; + + /* Check if opmode for regulator matches S5M8767_ENCTRL_USE_GPIO */ + for (i = 0; i < s5m8767->num_regulators; i++) { + const struct sec_opmode_data *opmode = &s5m8767->opmode[i]; + if (opmode->id == rdata->id) { + mode = s5m8767_opmode_reg[rdata->id][opmode->mode]; + break; + } + } + if (mode != S5M8767_ENCTRL_USE_GPIO) { + dev_warn(s5m8767->dev, + "ext-control for %s: mismatched op_mode (%x), ignoring\n", + rdata->reg_node->name, mode); + return; + } + + if (!gpio_is_valid(rdata->ext_control_gpio)) { + dev_warn(s5m8767->dev, + "ext-control for %s: GPIO not valid, ignoring\n", + rdata->reg_node->name); + return; + } + + config->ena_gpio = rdata->ext_control_gpio; + config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH; +} + +/* + * Turn on GPIO control over BUCK9. + */ +static int s5m8767_enable_ext_control(struct s5m8767_info *s5m8767, + struct regulator_dev *rdev) +{ + int id = rdev_get_id(rdev); + int ret, reg, enable_ctrl; + + if (id != S5M8767_BUCK9) + return -EINVAL; + + ret = s5m8767_get_register(s5m8767, id, ®, &enable_ctrl); + if (ret) + return ret; + + return regmap_update_bits(s5m8767->iodev->regmap_pmic, + reg, S5M8767_ENCTRL_MASK, + S5M8767_ENCTRL_USE_GPIO << S5M8767_ENCTRL_SHIFT); +} + + #ifdef CONFIG_OF static int s5m8767_pmic_dt_parse_dvs_gpio(struct sec_pmic_dev *iodev, struct sec_platform_data *pdata, @@ -520,6 +529,16 @@ static int s5m8767_pmic_dt_parse_ds_gpio(struct sec_pmic_dev *iodev, return 0; } +static void s5m8767_pmic_dt_parse_ext_control_gpio(struct sec_pmic_dev *iodev, + struct sec_regulator_data *rdata, + struct device_node *reg_np) +{ + rdata->ext_control_gpio = of_get_named_gpio(reg_np, + "s5m8767,pmic-ext-control-gpios", 0); + if (!gpio_is_valid(rdata->ext_control_gpio)) + rdata->ext_control_gpio = 0; +} + static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, struct sec_platform_data *pdata) { @@ -546,19 +565,13 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * pdata->num_regulators, GFP_KERNEL); - if (!rdata) { - dev_err(iodev->dev, - "could not allocate memory for regulator data\n"); + if (!rdata) return -ENOMEM; - } rmode = devm_kzalloc(&pdev->dev, sizeof(*rmode) * pdata->num_regulators, GFP_KERNEL); - if (!rmode) { - dev_err(iodev->dev, - "could not allocate memory for regulator mode\n"); + if (!rmode) return -ENOMEM; - } pdata->regulators = rdata; pdata->opmode = rmode; @@ -574,6 +587,8 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev, continue; } + s5m8767_pmic_dt_parse_ext_control_gpio(iodev, rdata, reg_np); + rdata->id = i; rdata->initdata = of_get_regulator_init_data( &pdev->dev, reg_np); @@ -922,6 +937,7 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) for (i = 0; i < pdata->num_regulators; i++) { const struct sec_voltage_desc *desc; int id = pdata->regulators[i].id; + int enable_reg, enable_val; desc = reg_voltage_map[id]; if (desc) { @@ -935,6 +951,12 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) regulators[id].vsel_mask = 0x3f; else regulators[id].vsel_mask = 0xff; + + s5m8767_get_register(s5m8767, id, &enable_reg, + &enable_val); + regulators[id].enable_reg = enable_reg; + regulators[id].enable_mask = S5M8767_ENCTRL_MASK; + regulators[id].enable_val = enable_val; } config.dev = s5m8767->dev; @@ -942,6 +964,9 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) config.driver_data = s5m8767; config.regmap = iodev->regmap_pmic; config.of_node = pdata->regulators[i].reg_node; + if (pdata->regulators[i].ext_control_gpio) + s5m8767_regulator_config_ext_control(s5m8767, + &pdata->regulators[i], &config); rdev[i] = devm_regulator_register(&pdev->dev, ®ulators[id], &config); @@ -951,6 +976,16 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) id); return ret; } + + if (pdata->regulators[i].ext_control_gpio) { + ret = s5m8767_enable_ext_control(s5m8767, rdev[i]); + if (ret < 0) { + dev_err(s5m8767->dev, + "failed to enable gpio control over %s: %d\n", + rdev[i]->desc->name, ret); + return ret; + } + } } return 0; diff --git a/drivers/regulator/st-pwm.c b/drivers/regulator/st-pwm.c new file mode 100644 index 000000000000..e367af1c5f9d --- /dev/null +++ b/drivers/regulator/st-pwm.c @@ -0,0 +1,190 @@ +/* + * Regulator driver for ST's PWM Regulators + * + * Copyright (C) 2014 - STMicroelectronics Inc. + * + * Author: Lee Jones <lee.jones@linaro.org> + * + * 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/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pwm.h> + +#define ST_PWM_REG_PERIOD 8448 + +struct st_pwm_regulator_pdata { + const struct regulator_desc *desc; + struct st_pwm_voltages *duty_cycle_table; +}; + +struct st_pwm_regulator_data { + const struct st_pwm_regulator_pdata *pdata; + struct pwm_device *pwm; + bool enabled; + int state; +}; + +struct st_pwm_voltages { + unsigned int uV; + unsigned int dutycycle; +}; + +static int st_pwm_regulator_get_voltage_sel(struct regulator_dev *dev) +{ + struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + + return drvdata->state; +} + +static int st_pwm_regulator_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + int dutycycle; + int ret; + + dutycycle = (ST_PWM_REG_PERIOD / 100) * + drvdata->pdata->duty_cycle_table[selector].dutycycle; + + ret = pwm_config(drvdata->pwm, dutycycle, ST_PWM_REG_PERIOD); + if (ret) { + dev_err(&dev->dev, "Failed to configure PWM\n"); + return ret; + } + + drvdata->state = selector; + + if (!drvdata->enabled) { + ret = pwm_enable(drvdata->pwm); + if (ret) { + dev_err(&dev->dev, "Failed to enable PWM\n"); + return ret; + } + drvdata->enabled = true; + } + + return 0; +} + +static int st_pwm_regulator_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev); + + if (selector >= dev->desc->n_voltages) + return -EINVAL; + + return drvdata->pdata->duty_cycle_table[selector].uV; +} + +static struct regulator_ops st_pwm_regulator_voltage_ops = { + .set_voltage_sel = st_pwm_regulator_set_voltage_sel, + .get_voltage_sel = st_pwm_regulator_get_voltage_sel, + .list_voltage = st_pwm_regulator_list_voltage, + .map_voltage = regulator_map_voltage_iterate, +}; + +static struct st_pwm_voltages b2105_duty_cycle_table[] = { + { .uV = 1114000, .dutycycle = 0, }, + { .uV = 1095000, .dutycycle = 10, }, + { .uV = 1076000, .dutycycle = 20, }, + { .uV = 1056000, .dutycycle = 30, }, + { .uV = 1036000, .dutycycle = 40, }, + { .uV = 1016000, .dutycycle = 50, }, + /* WARNING: Values above 50% duty-cycle cause boot failures. */ +}; + +static const struct regulator_desc b2105_desc = { + .name = "b2105-pwm-regulator", + .ops = &st_pwm_regulator_voltage_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(b2105_duty_cycle_table), + .supply_name = "pwm", +}; + +static const struct st_pwm_regulator_pdata b2105_info = { + .desc = &b2105_desc, + .duty_cycle_table = b2105_duty_cycle_table, +}; + +static struct of_device_id st_pwm_of_match[] = { + { .compatible = "st,b2105-pwm-regulator", .data = &b2105_info, }, + { }, +}; +MODULE_DEVICE_TABLE(of, st_pwm_of_match); + +static int st_pwm_regulator_probe(struct platform_device *pdev) +{ + struct st_pwm_regulator_data *drvdata; + struct regulator_dev *regulator; + struct regulator_config config = { }; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_match; + + if (!np) { + dev_err(&pdev->dev, "Device Tree node missing\n"); + return -EINVAL; + } + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + of_match = of_match_device(st_pwm_of_match, &pdev->dev); + if (!of_match) { + dev_err(&pdev->dev, "failed to match of device\n"); + return -ENODEV; + } + drvdata->pdata = of_match->data; + + config.init_data = of_get_regulator_init_data(&pdev->dev, np); + if (!config.init_data) + return -ENOMEM; + + config.of_node = np; + config.dev = &pdev->dev; + config.driver_data = drvdata; + + drvdata->pwm = devm_pwm_get(&pdev->dev, NULL); + if (IS_ERR(drvdata->pwm)) { + dev_err(&pdev->dev, "Failed to get PWM\n"); + return PTR_ERR(drvdata->pwm); + } + + regulator = devm_regulator_register(&pdev->dev, + drvdata->pdata->desc, &config); + if (IS_ERR(regulator)) { + dev_err(&pdev->dev, "Failed to register regulator %s\n", + drvdata->pdata->desc->name); + return PTR_ERR(regulator); + } + + return 0; +} + +static struct platform_driver st_pwm_regulator_driver = { + .driver = { + .name = "st-pwm-regulator", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(st_pwm_of_match), + }, + .probe = st_pwm_regulator_probe, +}; + +module_platform_driver(st_pwm_regulator_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>"); +MODULE_DESCRIPTION("ST PWM Regulator Driver"); +MODULE_ALIAS("platform:st_pwm-regulator"); diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index b187b6bba7ad..a2dabb575b97 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -54,8 +54,8 @@ struct ti_abb_info { /** * struct ti_abb_reg - Register description for ABB block - * @setup_reg: setup register offset from base - * @control_reg: control register offset from base + * @setup_off: setup register offset from base + * @control_off: control register offset from base * @sr2_wtcnt_value_mask: setup register- sr2_wtcnt_value mask * @fbb_sel_mask: setup register- FBB sel mask * @rbb_sel_mask: setup register- RBB sel mask @@ -64,8 +64,8 @@ struct ti_abb_info { * @opp_sel_mask: control register - mask for mode to operate */ struct ti_abb_reg { - u32 setup_reg; - u32 control_reg; + u32 setup_off; + u32 control_off; /* Setup register fields */ u32 sr2_wtcnt_value_mask; @@ -83,6 +83,8 @@ struct ti_abb_reg { * @rdesc: regulator descriptor * @clk: clock(usually sysclk) supplying ABB block * @base: base address of ABB block + * @setup_reg: setup register of ABB block + * @control_reg: control register of ABB block * @int_base: interrupt register base address * @efuse_base: (optional) efuse base address for ABB modes * @ldo_base: (optional) LDOVBB vset override base address @@ -99,6 +101,8 @@ struct ti_abb { struct regulator_desc rdesc; struct clk *clk; void __iomem *base; + void __iomem *setup_reg; + void __iomem *control_reg; void __iomem *int_base; void __iomem *efuse_base; void __iomem *ldo_base; @@ -118,20 +122,18 @@ struct ti_abb { * ti_abb_rmw() - handy wrapper to set specific register bits * @mask: mask for register field * @value: value shifted to mask location and written - * @offset: offset of register - * @base: base address + * @reg: register address * * Return: final register value (may be unused) */ -static inline u32 ti_abb_rmw(u32 mask, u32 value, u32 offset, - void __iomem *base) +static inline u32 ti_abb_rmw(u32 mask, u32 value, void __iomem *reg) { u32 val; - val = readl(base + offset); + val = readl(reg); val &= ~mask; val |= (value << __ffs(mask)) & mask; - writel(val, base + offset); + writel(val, reg); return val; } @@ -263,21 +265,19 @@ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, if (ret) goto out; - ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, regs->setup_reg, - abb->base); + ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, abb->setup_reg); switch (info->opp_sel) { case TI_ABB_SLOW_OPP: - ti_abb_rmw(regs->rbb_sel_mask, 1, regs->setup_reg, abb->base); + ti_abb_rmw(regs->rbb_sel_mask, 1, abb->setup_reg); break; case TI_ABB_FAST_OPP: - ti_abb_rmw(regs->fbb_sel_mask, 1, regs->setup_reg, abb->base); + ti_abb_rmw(regs->fbb_sel_mask, 1, abb->setup_reg); break; } /* program next state of ABB ldo */ - ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, regs->control_reg, - abb->base); + ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, abb->control_reg); /* * program LDO VBB vset override if needed for !bypass mode @@ -288,7 +288,7 @@ static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, ti_abb_program_ldovbb(dev, abb, info); /* Initiate ABB ldo change */ - ti_abb_rmw(regs->opp_change_mask, 1, regs->control_reg, abb->base); + ti_abb_rmw(regs->opp_change_mask, 1, abb->control_reg); /* Wait for ABB LDO to complete transition to new Bias setting */ ret = ti_abb_wait_txdone(dev, abb); @@ -490,8 +490,7 @@ static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb) dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__, clk_get_rate(abb->clk), sr2_wt_cnt_val); - ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, regs->setup_reg, - abb->base); + ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, abb->setup_reg); return 0; } @@ -508,32 +507,24 @@ static int ti_abb_init_table(struct device *dev, struct ti_abb *abb, struct regulator_init_data *rinit_data) { struct ti_abb_info *info; - const struct property *prop; - const __be32 *abb_info; const u32 num_values = 6; char *pname = "ti,abb_info"; - u32 num_entries, i; + u32 i; unsigned int *volt_table; - int min_uV = INT_MAX, max_uV = 0; + int num_entries, min_uV = INT_MAX, max_uV = 0; struct regulation_constraints *c = &rinit_data->constraints; - prop = of_find_property(dev->of_node, pname, NULL); - if (!prop) { - dev_err(dev, "No '%s' property?\n", pname); - return -ENODEV; - } - - if (!prop->value) { - dev_err(dev, "Empty '%s' property?\n", pname); - return -ENODATA; - } - /* * Each abb_info is a set of n-tuple, where n is num_values, consisting * of voltage and a set of detection logic for ABB information for that * voltage to apply. */ - num_entries = prop->length / sizeof(u32); + num_entries = of_property_count_u32_elems(dev->of_node, pname); + if (num_entries < 0) { + dev_err(dev, "No '%s' property?\n", pname); + return num_entries; + } + if (!num_entries || (num_entries % num_values)) { dev_err(dev, "All '%s' list entries need %d vals\n", pname, num_values); @@ -542,38 +533,38 @@ static int ti_abb_init_table(struct device *dev, struct ti_abb *abb, num_entries /= num_values; info = devm_kzalloc(dev, sizeof(*info) * num_entries, GFP_KERNEL); - if (!info) { - dev_err(dev, "Can't allocate info table for '%s' property\n", - pname); + if (!info) return -ENOMEM; - } + abb->info = info; volt_table = devm_kzalloc(dev, sizeof(unsigned int) * num_entries, GFP_KERNEL); - if (!volt_table) { - dev_err(dev, "Can't allocate voltage table for '%s' property\n", - pname); + if (!volt_table) return -ENOMEM; - } abb->rdesc.n_voltages = num_entries; abb->rdesc.volt_table = volt_table; /* We do not know where the OPP voltage is at the moment */ abb->current_info_idx = -EINVAL; - abb_info = prop->value; for (i = 0; i < num_entries; i++, info++, volt_table++) { u32 efuse_offset, rbb_mask, fbb_mask, vset_mask; u32 efuse_val; /* NOTE: num_values should equal to entries picked up here */ - *volt_table = be32_to_cpup(abb_info++); - info->opp_sel = be32_to_cpup(abb_info++); - efuse_offset = be32_to_cpup(abb_info++); - rbb_mask = be32_to_cpup(abb_info++); - fbb_mask = be32_to_cpup(abb_info++); - vset_mask = be32_to_cpup(abb_info++); + of_property_read_u32_index(dev->of_node, pname, i * num_values, + volt_table); + of_property_read_u32_index(dev->of_node, pname, + i * num_values + 1, &info->opp_sel); + of_property_read_u32_index(dev->of_node, pname, + i * num_values + 2, &efuse_offset); + of_property_read_u32_index(dev->of_node, pname, + i * num_values + 3, &rbb_mask); + of_property_read_u32_index(dev->of_node, pname, + i * num_values + 4, &fbb_mask); + of_property_read_u32_index(dev->of_node, pname, + i * num_values + 5, &vset_mask); dev_dbg(dev, "[%d]v=%d ABB=%d ef=0x%x rbb=0x%x fbb=0x%x vset=0x%x\n", @@ -648,8 +639,8 @@ static struct regulator_ops ti_abb_reg_ops = { /* Default ABB block offsets, IF this changes in future, create new one */ static const struct ti_abb_reg abb_regs_v1 = { /* WARNING: registers are wrongly documented in TRM */ - .setup_reg = 0x04, - .control_reg = 0x00, + .setup_off = 0x04, + .control_off = 0x00, .sr2_wtcnt_value_mask = (0xff << 8), .fbb_sel_mask = (0x01 << 2), @@ -661,8 +652,8 @@ static const struct ti_abb_reg abb_regs_v1 = { }; static const struct ti_abb_reg abb_regs_v2 = { - .setup_reg = 0x00, - .control_reg = 0x04, + .setup_off = 0x00, + .control_off = 0x04, .sr2_wtcnt_value_mask = (0xff << 8), .fbb_sel_mask = (0x01 << 2), @@ -673,9 +664,20 @@ static const struct ti_abb_reg abb_regs_v2 = { .opp_sel_mask = (0x03 << 0), }; +static const struct ti_abb_reg abb_regs_generic = { + .sr2_wtcnt_value_mask = (0xff << 8), + .fbb_sel_mask = (0x01 << 2), + .rbb_sel_mask = (0x01 << 1), + .sr2_en_mask = (0x01 << 0), + + .opp_change_mask = (0x01 << 2), + .opp_sel_mask = (0x03 << 0), +}; + static const struct of_device_id ti_abb_of_match[] = { {.compatible = "ti,abb-v1", .data = &abb_regs_v1}, {.compatible = "ti,abb-v2", .data = &abb_regs_v2}, + {.compatible = "ti,abb-v3", .data = &abb_regs_generic}, { }, }; @@ -722,11 +724,29 @@ static int ti_abb_probe(struct platform_device *pdev) abb->regs = match->data; /* Map ABB resources */ - pname = "base-address"; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); - abb->base = devm_ioremap_resource(dev, res); - if (IS_ERR(abb->base)) - return PTR_ERR(abb->base); + if (abb->regs->setup_off || abb->regs->control_off) { + pname = "base-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + abb->base = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->base)) + return PTR_ERR(abb->base); + + abb->setup_reg = abb->base + abb->regs->setup_off; + abb->control_reg = abb->base + abb->regs->control_off; + + } else { + pname = "control-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + abb->control_reg = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->control_reg)) + return PTR_ERR(abb->control_reg); + + pname = "setup-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + abb->setup_reg = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->setup_reg)) + return PTR_ERR(abb->setup_reg); + } pname = "int-address"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); @@ -860,7 +880,7 @@ skip_opt: platform_set_drvdata(pdev, rdev); /* Enable the ldo if not already done by bootloader */ - ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->regs->setup_reg, abb->base); + ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->setup_reg); return 0; } diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c index b3764f594ee9..f31f22e3e1bd 100644 --- a/drivers/regulator/tps51632-regulator.c +++ b/drivers/regulator/tps51632-regulator.c @@ -227,10 +227,8 @@ static struct tps51632_regulator_platform_data * struct device_node *np = dev->of_node; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(dev, "Memory alloc failed for platform data\n"); + if (!pdata) return NULL; - } pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node); if (!pdata->reg_init_data) { @@ -299,10 +297,8 @@ static int tps51632_probe(struct i2c_client *client, } tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); - if (!tps) { - dev_err(&client->dev, "Memory allocation failed\n"); + if (!tps) return -ENOMEM; - } tps->dev = &client->dev; tps->desc.name = client->name; diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index c3fa15a299b1..a1672044e519 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -299,10 +299,8 @@ static struct tps62360_regulator_platform_data * struct device_node *np = dev->of_node; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(dev, "Memory alloc failed for platform data\n"); + if (!pdata) return NULL; - } pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node); if (!pdata->reg_init_data) { @@ -377,11 +375,8 @@ static int tps62360_probe(struct i2c_client *client, } tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); - if (!tps) { - dev_err(&client->dev, "%s(): Memory allocation failed\n", - __func__); + if (!tps) return -ENOMEM; - } tps->en_discharge = pdata->en_discharge; tps->en_internal_pulldn = pdata->en_internal_pulldn; diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index 162a0fae20b3..98e66ce26723 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -359,7 +359,6 @@ static struct regulator_ops tps6507x_pmic_ops = { .map_voltage = regulator_map_voltage_ascend, }; -#ifdef CONFIG_OF static struct of_regulator_match tps6507x_matches[] = { { .name = "VDCDC1"}, { .name = "VDCDC2"}, @@ -381,12 +380,10 @@ static struct tps6507x_board *tps6507x_parse_dt_reg_data( tps_board = devm_kzalloc(&pdev->dev, sizeof(*tps_board), GFP_KERNEL); - if (!tps_board) { - dev_err(&pdev->dev, "Failure to alloc pdata for regulators.\n"); + if (!tps_board) return NULL; - } - regulators = of_find_node_by_name(np, "regulators"); + regulators = of_get_child_by_name(np, "regulators"); if (!regulators) { dev_err(&pdev->dev, "regulator node not found\n"); return NULL; @@ -396,6 +393,7 @@ static struct tps6507x_board *tps6507x_parse_dt_reg_data( matches = tps6507x_matches; ret = of_regulator_match(&pdev->dev, regulators, matches, count); + of_node_put(regulators); if (ret < 0) { dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); @@ -406,10 +404,8 @@ static struct tps6507x_board *tps6507x_parse_dt_reg_data( reg_data = devm_kzalloc(&pdev->dev, (sizeof(struct regulator_init_data) * TPS6507X_NUM_REGULATOR), GFP_KERNEL); - if (!reg_data) { - dev_err(&pdev->dev, "Failure to alloc init data for regulators.\n"); + if (!reg_data) return NULL; - } tps_board->tps6507x_pmic_init_data = reg_data; @@ -424,15 +420,7 @@ static struct tps6507x_board *tps6507x_parse_dt_reg_data( return tps_board; } -#else -static inline struct tps6507x_board *tps6507x_parse_dt_reg_data( - struct platform_device *pdev, - struct of_regulator_match **tps6507x_reg_matches) -{ - *tps6507x_reg_matches = NULL; - return NULL; -} -#endif + static int tps6507x_pmic_probe(struct platform_device *pdev) { struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); @@ -453,9 +441,10 @@ static int tps6507x_pmic_probe(struct platform_device *pdev) */ tps_board = dev_get_platdata(tps6507x_dev->dev); - if (!tps_board && tps6507x_dev->dev->of_node) + if (IS_ENABLED(CONFIG_OF) && !tps_board && + tps6507x_dev->dev->of_node) tps_board = tps6507x_parse_dt_reg_data(pdev, - &tps6507x_reg_matches); + &tps6507x_reg_matches); if (!tps_board) return -EINVAL; @@ -481,7 +470,7 @@ static int tps6507x_pmic_probe(struct platform_device *pdev) tps->info[i] = info; if (init_data->driver_data) { struct tps6507x_reg_platform_data *data = - init_data->driver_data; + init_data->driver_data; tps->info[i]->defdcdc_default = data->defdcdc_default; } diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 676f75548f00..2e92ef68574d 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -168,17 +168,13 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data( tps65090_pdata = devm_kzalloc(&pdev->dev, sizeof(*tps65090_pdata), GFP_KERNEL); - if (!tps65090_pdata) { - dev_err(&pdev->dev, "Memory alloc for tps65090_pdata failed\n"); + if (!tps65090_pdata) return ERR_PTR(-ENOMEM); - } reg_pdata = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * sizeof(*reg_pdata), GFP_KERNEL); - if (!reg_pdata) { - dev_err(&pdev->dev, "Memory alloc for reg_pdata failed\n"); + if (!reg_pdata) return ERR_PTR(-ENOMEM); - } regulators = of_get_child_by_name(np, "regulators"); if (!regulators) { @@ -188,6 +184,7 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data( ret = of_regulator_match(&pdev->dev, regulators, tps65090_matches, ARRAY_SIZE(tps65090_matches)); + of_node_put(regulators); if (ret < 0) { dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); @@ -252,10 +249,8 @@ static int tps65090_regulator_probe(struct platform_device *pdev) pmic = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * sizeof(*pmic), GFP_KERNEL); - if (!pmic) { - dev_err(&pdev->dev, "mem alloc for pmic failed\n"); + if (!pmic) return -ENOMEM; - } for (num = 0; num < TPS65090_REGULATOR_MAX; num++) { tps_pdata = tps65090_pdata->reg_pdata[num]; diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 9ea1bf26bd13..10b78d2b766a 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -187,7 +187,7 @@ static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev) struct device_node *regs; int i, count; - regs = of_find_node_by_name(node, "regulators"); + regs = of_get_child_by_name(node, "regulators"); if (!regs) return NULL; @@ -202,7 +202,7 @@ static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev) return NULL; for (i = 0; i < count; i++) { - if (!reg_matches[i].init_data || !reg_matches[i].of_node) + if (!reg_matches[i].of_node) continue; pdata->tps65217_init_data[i] = reg_matches[i].init_data; @@ -222,7 +222,6 @@ static int tps65217_regulator_probe(struct platform_device *pdev) { struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent); struct tps65217_board *pdata = dev_get_platdata(tps->dev); - struct regulator_init_data *reg_data; struct regulator_dev *rdev; struct regulator_config config = { }; int i; @@ -243,19 +242,9 @@ static int tps65217_regulator_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tps); for (i = 0; i < TPS65217_NUM_REGULATOR; i++) { - - reg_data = pdata->tps65217_init_data[i]; - - /* - * Regulator API handles empty constraints but not NULL - * constraints - */ - if (!reg_data) - continue; - /* Register the regulators */ config.dev = tps->dev; - config.init_data = reg_data; + config.init_data = pdata->tps65217_init_data[i]; config.driver_data = tps; config.regmap = tps->regmap; if (tps->dev->of_node) diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c new file mode 100644 index 000000000000..cec72fa71d1d --- /dev/null +++ b/drivers/regulator/tps65218-regulator.c @@ -0,0 +1,285 @@ +/* + * tps65218-regulator.c + * + * Regulator driver for TPS65218 PMIC + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.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 expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <linux/regulator/of_regulator.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/tps65218.h> + +static unsigned int tps65218_ramp_delay = 4000; + +enum tps65218_regulators { DCDC1, DCDC2, DCDC3, DCDC4, DCDC5, DCDC6, LDO1 }; + +#define TPS65218_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _er, _em, _t, \ + _lr, _nlr) \ + { \ + .name = _name, \ + .id = _id, \ + .ops = &_ops, \ + .n_voltages = _n, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = _vr, \ + .vsel_mask = _vm, \ + .enable_reg = _er, \ + .enable_mask = _em, \ + .volt_table = _t, \ + .linear_ranges = _lr, \ + .n_linear_ranges = _nlr, \ + } \ + +#define TPS65218_INFO(_id, _nm, _min, _max) \ + { \ + .id = _id, \ + .name = _nm, \ + .min_uV = _min, \ + .max_uV = _max, \ + } + +static const struct regulator_linear_range dcdc1_dcdc2_ranges[] = { + REGULATOR_LINEAR_RANGE(850000, 0x0, 0x32, 10000), + REGULATOR_LINEAR_RANGE(1375000, 0x33, 0x3f, 25000), +}; + +static const struct regulator_linear_range ldo1_dcdc3_ranges[] = { + REGULATOR_LINEAR_RANGE(900000, 0x0, 0x1a, 25000), + REGULATOR_LINEAR_RANGE(1600000, 0x1b, 0x3f, 50000), +}; + +static const struct regulator_linear_range dcdc4_ranges[] = { + REGULATOR_LINEAR_RANGE(1175000, 0x0, 0xf, 25000), + REGULATOR_LINEAR_RANGE(1550000, 0x10, 0x34, 50000), +}; + +static struct tps_info tps65218_pmic_regs[] = { + TPS65218_INFO(0, "DCDC1", 850000, 167500), + TPS65218_INFO(1, "DCDC2", 850000, 1675000), + TPS65218_INFO(2, "DCDC3", 900000, 3400000), + TPS65218_INFO(3, "DCDC4", 1175000, 3400000), + TPS65218_INFO(4, "DCDC5", 1000000, 1000000), + TPS65218_INFO(5, "DCDC6", 1800000, 1800000), + TPS65218_INFO(6, "LDO1", 900000, 3400000), +}; + +#define TPS65218_OF_MATCH(comp, label) \ + { \ + .compatible = comp, \ + .data = &label, \ + } + +static const struct of_device_id tps65218_of_match[] = { + TPS65218_OF_MATCH("ti,tps65218-dcdc1", tps65218_pmic_regs[DCDC1]), + TPS65218_OF_MATCH("ti,tps65218-dcdc2", tps65218_pmic_regs[DCDC2]), + TPS65218_OF_MATCH("ti,tps65218-dcdc3", tps65218_pmic_regs[DCDC3]), + TPS65218_OF_MATCH("ti,tps65218-dcdc4", tps65218_pmic_regs[DCDC4]), + TPS65218_OF_MATCH("ti,tps65218-dcdc5", tps65218_pmic_regs[DCDC5]), + TPS65218_OF_MATCH("ti,tps65218-dcdc6", tps65218_pmic_regs[DCDC6]), + TPS65218_OF_MATCH("ti,tps65218-ldo1", tps65218_pmic_regs[LDO1]), + { } +}; +MODULE_DEVICE_TABLE(of, tps65218_of_match); + +static int tps65218_pmic_set_voltage_sel(struct regulator_dev *dev, + unsigned selector) +{ + int ret; + struct tps65218 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + /* Set the voltage based on vsel value and write protect level is 2 */ + ret = tps65218_set_bits(tps, dev->desc->vsel_reg, dev->desc->vsel_mask, + selector, TPS65218_PROTECT_L1); + + /* Set GO bit for DCDC1/2 to initiate voltage transistion */ + switch (rid) { + case TPS65218_DCDC_1: + case TPS65218_DCDC_2: + ret = tps65218_set_bits(tps, TPS65218_REG_CONTRL_SLEW_RATE, + TPS65218_SLEW_RATE_GO, + TPS65218_SLEW_RATE_GO, + TPS65218_PROTECT_L1); + break; + } + + return ret; +} + +static int tps65218_pmic_enable(struct regulator_dev *dev) +{ + struct tps65218 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + if (rid < TPS65218_DCDC_1 || rid > TPS65218_LDO_1) + return -EINVAL; + + /* Enable the regulator and password protection is level 1 */ + return tps65218_set_bits(tps, dev->desc->enable_reg, + dev->desc->enable_mask, dev->desc->enable_mask, + TPS65218_PROTECT_L1); +} + +static int tps65218_pmic_disable(struct regulator_dev *dev) +{ + struct tps65218 *tps = rdev_get_drvdata(dev); + unsigned int rid = rdev_get_id(dev); + + if (rid < TPS65218_DCDC_1 || rid > TPS65218_LDO_1) + return -EINVAL; + + /* Disable the regulator and password protection is level 1 */ + return tps65218_clear_bits(tps, dev->desc->enable_reg, + dev->desc->enable_mask, TPS65218_PROTECT_L1); +} + +static int tps65218_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + int old_uv, new_uv; + + old_uv = regulator_list_voltage_linear_range(rdev, old_selector); + if (old_uv < 0) + return old_uv; + + new_uv = regulator_list_voltage_linear_range(rdev, new_selector); + if (new_uv < 0) + return new_uv; + + return DIV_ROUND_UP(abs(old_uv - new_uv), tps65218_ramp_delay); +} + +/* Operations permitted on DCDC1, DCDC2 */ +static struct regulator_ops tps65218_dcdc12_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = tps65218_pmic_enable, + .disable = tps65218_pmic_disable, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = tps65218_pmic_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_time_sel = tps65218_set_voltage_time_sel, +}; + +/* Operations permitted on DCDC3, DCDC4 and LDO1 */ +static struct regulator_ops tps65218_ldo1_dcdc34_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = tps65218_pmic_enable, + .disable = tps65218_pmic_disable, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = tps65218_pmic_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, +}; + +/* Operations permitted on DCDC5, DCDC6 */ +static struct regulator_ops tps65218_dcdc56_pmic_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = tps65218_pmic_enable, + .disable = tps65218_pmic_disable, +}; + +static const struct regulator_desc regulators[] = { + TPS65218_REGULATOR("DCDC1", TPS65218_DCDC_1, tps65218_dcdc12_ops, 64, + TPS65218_REG_CONTROL_DCDC1, + TPS65218_CONTROL_DCDC1_MASK, + TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC1_EN, NULL, + dcdc1_dcdc2_ranges, 2), + TPS65218_REGULATOR("DCDC2", TPS65218_DCDC_2, tps65218_dcdc12_ops, 64, + TPS65218_REG_CONTROL_DCDC2, + TPS65218_CONTROL_DCDC2_MASK, + TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC2_EN, NULL, + dcdc1_dcdc2_ranges, 2), + TPS65218_REGULATOR("DCDC3", TPS65218_DCDC_3, tps65218_ldo1_dcdc34_ops, + 64, TPS65218_REG_CONTROL_DCDC3, + TPS65218_CONTROL_DCDC3_MASK, TPS65218_REG_ENABLE1, + TPS65218_ENABLE1_DC3_EN, NULL, + ldo1_dcdc3_ranges, 2), + TPS65218_REGULATOR("DCDC4", TPS65218_DCDC_4, tps65218_ldo1_dcdc34_ops, + 53, TPS65218_REG_CONTROL_DCDC4, + TPS65218_CONTROL_DCDC4_MASK, + TPS65218_REG_ENABLE1, TPS65218_ENABLE1_DC4_EN, NULL, + dcdc4_ranges, 2), + TPS65218_REGULATOR("DCDC5", TPS65218_DCDC_5, tps65218_dcdc56_pmic_ops, + 1, -1, -1, TPS65218_REG_ENABLE1, + TPS65218_ENABLE1_DC5_EN, NULL, NULL, 0), + TPS65218_REGULATOR("DCDC6", TPS65218_DCDC_6, tps65218_dcdc56_pmic_ops, + 1, -1, -1, TPS65218_REG_ENABLE1, + TPS65218_ENABLE1_DC6_EN, NULL, NULL, 0), + TPS65218_REGULATOR("LDO1", TPS65218_LDO_1, tps65218_ldo1_dcdc34_ops, 64, + TPS65218_REG_CONTROL_DCDC4, + TPS65218_CONTROL_LDO1_MASK, TPS65218_REG_ENABLE2, + TPS65218_ENABLE2_LDO1_EN, NULL, ldo1_dcdc3_ranges, + 2), +}; + +static int tps65218_regulator_probe(struct platform_device *pdev) +{ + struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent); + struct regulator_init_data *init_data; + const struct tps_info *template; + struct regulator_dev *rdev; + const struct of_device_id *match; + struct regulator_config config = { }; + int id; + + match = of_match_device(tps65218_of_match, &pdev->dev); + if (!match) + return -ENODEV; + + template = match->data; + id = template->id; + init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); + + platform_set_drvdata(pdev, tps); + + tps->info[id] = &tps65218_pmic_regs[id]; + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = tps; + config.regmap = tps->regmap; + + rdev = devm_regulator_register(&pdev->dev, ®ulators[id], &config); + if (IS_ERR(rdev)) { + dev_err(tps->dev, "failed to register %s regulator\n", + pdev->name); + return PTR_ERR(rdev); + } + + return 0; +} + +static struct platform_driver tps65218_regulator_driver = { + .driver = { + .name = "tps65218-pmic", + .owner = THIS_MODULE, + .of_match_table = tps65218_of_match, + }, + .probe = tps65218_regulator_probe, +}; + +module_platform_driver(tps65218_regulator_driver); + +MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>"); +MODULE_DESCRIPTION("TPS65218 voltage regulator driver"); +MODULE_ALIAS("platform:tps65218-pmic"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c index 9f6bfda711b7..5b494db9f95c 100644 --- a/drivers/regulator/tps6524x-regulator.c +++ b/drivers/regulator/tps6524x-regulator.c @@ -593,10 +593,9 @@ static int pmic_probe(struct spi_device *spi) } hw = devm_kzalloc(&spi->dev, sizeof(struct tps6524x), GFP_KERNEL); - if (!hw) { - dev_err(dev, "cannot allocate regulator private data\n"); + if (!hw) return -ENOMEM; - } + spi_set_drvdata(spi, hw); memset(hw, 0, sizeof(struct tps6524x)); diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 0485d47f0d8a..32f38a63d944 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -363,10 +363,8 @@ static struct tps6586x_platform_data *tps6586x_parse_regulator_dt( } pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(&pdev->dev, "Memory alloction failed\n"); + if (!pdata) return NULL; - } for (i = 0; i < num; i++) { int id; @@ -398,7 +396,7 @@ static int tps6586x_regulator_probe(struct platform_device *pdev) { struct tps6586x_regulator *ri = NULL; struct regulator_config config = { }; - struct regulator_dev **rdev; + struct regulator_dev *rdev; struct regulator_init_data *reg_data; struct tps6586x_platform_data *pdata; struct of_regulator_match *tps6586x_reg_matches = NULL; @@ -418,13 +416,6 @@ static int tps6586x_regulator_probe(struct platform_device *pdev) return -ENODEV; } - rdev = devm_kzalloc(&pdev->dev, TPS6586X_ID_MAX_REGULATOR * - sizeof(*rdev), GFP_KERNEL); - if (!rdev) { - dev_err(&pdev->dev, "Mmemory alloc failed\n"); - return -ENOMEM; - } - version = tps6586x_get_version(pdev->dev.parent); for (id = 0; id < TPS6586X_ID_MAX_REGULATOR; ++id) { @@ -451,12 +442,11 @@ static int tps6586x_regulator_probe(struct platform_device *pdev) if (tps6586x_reg_matches) config.of_node = tps6586x_reg_matches[id].of_node; - rdev[id] = devm_regulator_register(&pdev->dev, &ri->desc, - &config); - if (IS_ERR(rdev[id])) { + rdev = devm_regulator_register(&pdev->dev, &ri->desc, &config); + if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", ri->desc.name); - return PTR_ERR(rdev[id]); + return PTR_ERR(rdev); } if (reg_data) { diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index f50dd847eebc..fa7db8847578 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -1011,11 +1011,8 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( pmic_plat_data = devm_kzalloc(&pdev->dev, sizeof(*pmic_plat_data), GFP_KERNEL); - - if (!pmic_plat_data) { - dev_err(&pdev->dev, "Failure to alloc pdata for regulators.\n"); + if (!pmic_plat_data) return NULL; - } np = of_node_get(pdev->dev.parent->of_node); regulators = of_get_child_by_name(np, "regulators"); @@ -1098,10 +1095,8 @@ static int tps65910_probe(struct platform_device *pdev) } pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); - if (!pmic) { - dev_err(&pdev->dev, "Memory allocation failed for pmic\n"); + if (!pmic) return -ENOMEM; - } pmic->mfd = tps65910; platform_set_drvdata(pdev, pmic); @@ -1130,24 +1125,18 @@ static int tps65910_probe(struct platform_device *pdev) pmic->desc = devm_kzalloc(&pdev->dev, pmic->num_regulators * sizeof(struct regulator_desc), GFP_KERNEL); - if (!pmic->desc) { - dev_err(&pdev->dev, "Memory alloc fails for desc\n"); + if (!pmic->desc) return -ENOMEM; - } pmic->info = devm_kzalloc(&pdev->dev, pmic->num_regulators * sizeof(struct tps_info *), GFP_KERNEL); - if (!pmic->info) { - dev_err(&pdev->dev, "Memory alloc fails for info\n"); + if (!pmic->info) return -ENOMEM; - } pmic->rdev = devm_kzalloc(&pdev->dev, pmic->num_regulators * sizeof(struct regulator_dev *), GFP_KERNEL); - if (!pmic->rdev) { - dev_err(&pdev->dev, "Memory alloc fails for rdev\n"); + if (!pmic->rdev) return -ENOMEM; - } for (i = 0; i < pmic->num_regulators && i < TPS65910_NUM_REGS; i++, info++) { diff --git a/drivers/regulator/tps80031-regulator.c b/drivers/regulator/tps80031-regulator.c index 71f457a42623..26aa6d9c308f 100644 --- a/drivers/regulator/tps80031-regulator.c +++ b/drivers/regulator/tps80031-regulator.c @@ -115,7 +115,7 @@ static int tps80031_reg_is_enabled(struct regulator_dev *rdev) ri->rinfo->state_reg, ret); return ret; } - return ((reg_val & TPS80031_STATE_MASK) == TPS80031_STATE_ON); + return (reg_val & TPS80031_STATE_MASK) == TPS80031_STATE_ON; } static int tps80031_reg_enable(struct regulator_dev *rdev) @@ -693,10 +693,8 @@ static int tps80031_regulator_probe(struct platform_device *pdev) pmic = devm_kzalloc(&pdev->dev, TPS80031_REGULATOR_MAX * sizeof(*pmic), GFP_KERNEL); - if (!pmic) { - dev_err(&pdev->dev, "mem alloc for pmic failed\n"); + if (!pmic) return -ENOMEM; - } for (num = 0; num < TPS80031_REGULATOR_MAX; ++num) { tps_pdata = pdata->regulator_pdata[num]; diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 04cf9c16ef23..0d88a82ab2a2 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -469,10 +469,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev) dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), GFP_KERNEL); - if (dcdc == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!dcdc) return -ENOMEM; - } dcdc->wm831x = wm831x; @@ -622,10 +620,8 @@ static int wm831x_buckp_probe(struct platform_device *pdev) dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), GFP_KERNEL); - if (dcdc == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!dcdc) return -ENOMEM; - } dcdc->wm831x = wm831x; @@ -752,10 +748,8 @@ static int wm831x_boostp_probe(struct platform_device *pdev) return -ENODEV; dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), GFP_KERNEL); - if (dcdc == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!dcdc) return -ENOMEM; - } dcdc->wm831x = wm831x; @@ -842,10 +836,8 @@ static int wm831x_epe_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Probing EPE%d\n", id + 1); dcdc = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_dcdc), GFP_KERNEL); - if (dcdc == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!dcdc) return -ENOMEM; - } dcdc->wm831x = wm831x; diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 0339b886df5d..72e385e76a9d 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -165,10 +165,8 @@ static int wm831x_isink_probe(struct platform_device *pdev) isink = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_isink), GFP_KERNEL); - if (isink == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!isink) return -ENOMEM; - } isink->wm831x = wm831x; diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 46d6700467b5..eca0eeb78acd 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -235,10 +235,8 @@ static int wm831x_gp_ldo_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ldo), GFP_KERNEL); - if (ldo == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!ldo) return -ENOMEM; - } ldo->wm831x = wm831x; @@ -447,10 +445,8 @@ static int wm831x_aldo_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ldo), GFP_KERNEL); - if (ldo == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!ldo) return -ENOMEM; - } ldo->wm831x = wm831x; @@ -594,10 +590,8 @@ static int wm831x_alive_ldo_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ldo), GFP_KERNEL); - if (ldo == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!ldo) return -ENOMEM; - } ldo->wm831x = wm831x; diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index de7b9c73e3fa..7ec7c390eeda 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -361,7 +361,7 @@ static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) sel = regulator_map_voltage_linear(rdev, uV, uV); if (sel < 0) - return -EINVAL; + return sel; /* all DCDCs have same mV bits */ val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; @@ -574,7 +574,7 @@ static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) sel = regulator_map_voltage_linear_range(rdev, uV, uV); if (sel < 0) - return -EINVAL; + return sel; /* all LDOs have same mV bits */ val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 71c5911f2e71..c24346db8a71 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -134,10 +134,8 @@ static int wm8994_ldo_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); ldo = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_ldo), GFP_KERNEL); - if (ldo == NULL) { - dev_err(&pdev->dev, "Unable to allocate private data\n"); + if (!ldo) return -ENOMEM; - } ldo->wm8994 = wm8994; ldo->supply = wm8994_ldo_consumer[id]; diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 7afd373b9595..c4cde9c08f1f 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -580,10 +580,12 @@ static int s3c_rtc_suspend(struct device *dev) clk_enable(rtc_clk); /* save TICNT for anyone using periodic interrupts */ - ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); if (s3c_rtc_cpu_type == TYPE_S3C64XX) { ticnt_en_save = readw(s3c_rtc_base + S3C2410_RTCCON); ticnt_en_save &= S3C64XX_RTCCON_TICEN; + ticnt_save = readl(s3c_rtc_base + S3C2410_TICNT); + } else { + ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT); } s3c_rtc_enable(pdev, 0); @@ -605,10 +607,15 @@ static int s3c_rtc_resume(struct device *dev) clk_enable(rtc_clk); s3c_rtc_enable(pdev, 1); - writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); - if (s3c_rtc_cpu_type == TYPE_S3C64XX && ticnt_en_save) { - tmp = readw(s3c_rtc_base + S3C2410_RTCCON); - writew(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON); + if (s3c_rtc_cpu_type == TYPE_S3C64XX) { + writel(ticnt_save, s3c_rtc_base + S3C2410_TICNT); + if (ticnt_en_save) { + tmp = readw(s3c_rtc_base + S3C2410_RTCCON); + writew(tmp | ticnt_en_save, + s3c_rtc_base + S3C2410_RTCCON); + } + } else { + writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT); } if (device_may_wakeup(dev) && wake_en) { diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index eb5d22795c47..5af7f0bd6125 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -922,7 +922,7 @@ static int __init con3215_init(void) raw3215_freelist = req; } - cdev = ccw_device_probe_console(); + cdev = ccw_device_create_console(&raw3215_ccw_driver); if (IS_ERR(cdev)) return -ENODEV; @@ -932,6 +932,12 @@ static int __init con3215_init(void) cdev->handler = raw3215_irq; raw->flags |= RAW3215_FIXED; + if (ccw_device_enable_console(cdev)) { + ccw_device_destroy_console(cdev); + raw3215_free_info(raw); + raw3215[0] = NULL; + return -ENODEV; + } /* Request the console irq */ if (raw3215_startup(raw) != 0) { diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 699fd3e363df..75ffe9980c3e 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -7,6 +7,7 @@ * Copyright IBM Corp. 2003, 2009 */ +#include <linux/module.h> #include <linux/console.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -30,6 +31,9 @@ static struct raw3270_fn con3270_fn; +static bool auto_update = 1; +module_param(auto_update, bool, 0); + /* * Main 3270 console view data structure. */ @@ -204,6 +208,8 @@ con3270_update(struct con3270 *cp) struct string *s, *n; int rc; + if (!auto_update && !raw3270_view_active(&cp->view)) + return; if (cp->view.dev) raw3270_activate_view(&cp->view); @@ -529,6 +535,7 @@ con3270_flush(void) if (!cp->view.dev) return; raw3270_pm_unfreeze(&cp->view); + raw3270_activate_view(&cp->view); spin_lock_irqsave(&cp->view.lock, flags); con3270_wait_write(cp); cp->nr_up = 0; @@ -576,7 +583,6 @@ static struct console con3270 = { static int __init con3270_init(void) { - struct ccw_device *cdev; struct raw3270 *rp; void *cbuf; int i; @@ -591,10 +597,7 @@ con3270_init(void) cpcmd("TERM AUTOCR OFF", NULL, 0, NULL); } - cdev = ccw_device_probe_console(); - if (IS_ERR(cdev)) - return -ENODEV; - rp = raw3270_setup_console(cdev); + rp = raw3270_setup_console(); if (IS_ERR(rp)) return PTR_ERR(rp); diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 2cdec21e8924..9f849df4381e 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -276,6 +276,15 @@ __raw3270_start(struct raw3270 *rp, struct raw3270_view *view, } int +raw3270_view_active(struct raw3270_view *view) +{ + struct raw3270 *rp = view->dev; + + return rp && rp->view == view && + !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags); +} + +int raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) { unsigned long flags; @@ -776,22 +785,37 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) } #ifdef CONFIG_TN3270_CONSOLE +/* Tentative definition - see below for actual definition. */ +static struct ccw_driver raw3270_ccw_driver; + /* * Setup 3270 device configured as console. */ -struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev) +struct raw3270 __init *raw3270_setup_console(void) { + struct ccw_device *cdev; unsigned long flags; struct raw3270 *rp; char *ascebc; int rc; + cdev = ccw_device_create_console(&raw3270_ccw_driver); + if (IS_ERR(cdev)) + return ERR_CAST(cdev); + rp = kzalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA); ascebc = kzalloc(256, GFP_KERNEL); rc = raw3270_setup_device(cdev, rp, ascebc); if (rc) return ERR_PTR(rc); set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags); + + rc = ccw_device_enable_console(cdev); + if (rc) { + ccw_device_destroy_console(cdev); + return ERR_PTR(rc); + } + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); do { __raw3270_reset_device(rp); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index 7b73ff8c1bd7..e1e41c2861fb 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -173,6 +173,7 @@ int raw3270_start_locked(struct raw3270_view *, struct raw3270_request *); int raw3270_start_irq(struct raw3270_view *, struct raw3270_request *); int raw3270_reset(struct raw3270_view *); struct raw3270_view *raw3270_view(struct raw3270_view *); +int raw3270_view_active(struct raw3270_view *); /* Reference count inliner for view structures. */ static inline void @@ -190,7 +191,7 @@ raw3270_put_view(struct raw3270_view *view) wake_up(&raw3270_wait_queue); } -struct raw3270 *raw3270_setup_console(struct ccw_device *cdev); +struct raw3270 *raw3270_setup_console(void); void raw3270_wait_cons_dev(struct raw3270 *); /* Notifier for device addition/removal */ diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index 82f2c389b4d1..14196ea0fdf3 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c @@ -20,7 +20,9 @@ struct read_info_sccb { struct sccb_header header; /* 0-7 */ u16 rnmax; /* 8-9 */ u8 rnsize; /* 10 */ - u8 _reserved0[24 - 11]; /* 11-15 */ + u8 _reserved0[16 - 11]; /* 11-15 */ + u16 ncpurl; /* 16-17 */ + u8 _reserved7[24 - 18]; /* 18-23 */ u8 loadparm[8]; /* 24-31 */ u8 _reserved1[48 - 32]; /* 32-47 */ u64 facilities; /* 48-55 */ @@ -32,13 +34,16 @@ struct read_info_sccb { u8 _reserved4[100 - 92]; /* 92-99 */ u32 rnsize2; /* 100-103 */ u64 rnmax2; /* 104-111 */ - u8 _reserved5[4096 - 112]; /* 112-4095 */ + u8 _reserved5[120 - 112]; /* 112-119 */ + u16 hcpua; /* 120-121 */ + u8 _reserved6[4096 - 122]; /* 122-4095 */ } __packed __aligned(PAGE_SIZE); static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata; static unsigned int sclp_con_has_vt220 __initdata; static unsigned int sclp_con_has_linemode __initdata; static unsigned long sclp_hsa_size; +static unsigned int sclp_max_cpu; static struct sclp_ipl_info sclp_ipl_info; u64 sclp_facilities; @@ -102,6 +107,15 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb) sclp_rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2; sclp_rzm <<= 20; + if (!sccb->hcpua) { + if (MACHINE_IS_VM) + sclp_max_cpu = 64; + else + sclp_max_cpu = sccb->ncpurl; + } else { + sclp_max_cpu = sccb->hcpua + 1; + } + /* Save IPL information */ sclp_ipl_info.is_valid = 1; if (sccb->flags & 0x2) @@ -129,6 +143,11 @@ unsigned long long sclp_get_rzm(void) return sclp_rzm; } +unsigned int sclp_get_max_cpu(void) +{ + return sclp_max_cpu; +} + /* * This function will be called after sclp_facilities_detect(), which gets * called from early.c code. The sclp_facilities_detect() function retrieves @@ -184,9 +203,9 @@ static long __init sclp_hsa_size_init(struct sdias_sccb *sccb) sccb_init_eq_size(sccb); if (sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_DATA, sccb)) return -EIO; - if (sccb->evbuf.blk_cnt != 0) - return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE; - return 0; + if (sccb->evbuf.blk_cnt == 0) + return 0; + return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE; } static long __init sclp_hsa_copy_wait(struct sccb_header *sccb) @@ -195,6 +214,8 @@ static long __init sclp_hsa_copy_wait(struct sccb_header *sccb) sccb->length = PAGE_SIZE; if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb)) return -EIO; + if (((struct sdias_sccb *) sccb)->evbuf.blk_cnt == 0) + return 0; return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE; } diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index f055df0b167f..445564c790f6 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -186,55 +186,71 @@ void airq_iv_release(struct airq_iv *iv) EXPORT_SYMBOL(airq_iv_release); /** - * airq_iv_alloc_bit - allocate an irq bit from an interrupt vector + * airq_iv_alloc - allocate irq bits from an interrupt vector * @iv: pointer to an interrupt vector structure + * @num: number of consecutive irq bits to allocate * - * Returns the bit number of the allocated irq, or -1UL if no bit - * is available or the AIRQ_IV_ALLOC flag has not been specified + * Returns the bit number of the first irq in the allocated block of irqs, + * or -1UL if no bit is available or the AIRQ_IV_ALLOC flag has not been + * specified */ -unsigned long airq_iv_alloc_bit(struct airq_iv *iv) +unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num) { - unsigned long bit; + unsigned long bit, i; - if (!iv->avail) + if (!iv->avail || num == 0) return -1UL; spin_lock(&iv->lock); bit = find_first_bit_inv(iv->avail, iv->bits); - if (bit < iv->bits) { - clear_bit_inv(bit, iv->avail); - if (bit >= iv->end) - iv->end = bit + 1; - } else + while (bit + num <= iv->bits) { + for (i = 1; i < num; i++) + if (!test_bit_inv(bit + i, iv->avail)) + break; + if (i >= num) { + /* Found a suitable block of irqs */ + for (i = 0; i < num; i++) + clear_bit_inv(bit + i, iv->avail); + if (bit + num >= iv->end) + iv->end = bit + num + 1; + break; + } + bit = find_next_bit_inv(iv->avail, iv->bits, bit + i + 1); + } + if (bit + num > iv->bits) bit = -1UL; spin_unlock(&iv->lock); return bit; } -EXPORT_SYMBOL(airq_iv_alloc_bit); +EXPORT_SYMBOL(airq_iv_alloc); /** - * airq_iv_free_bit - free an irq bit of an interrupt vector + * airq_iv_free - free irq bits of an interrupt vector * @iv: pointer to interrupt vector structure - * @bit: number of the irq bit to free + * @bit: number of the first irq bit to free + * @num: number of consecutive irq bits to free */ -void airq_iv_free_bit(struct airq_iv *iv, unsigned long bit) +void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num) { - if (!iv->avail) + unsigned long i; + + if (!iv->avail || num == 0) return; spin_lock(&iv->lock); - /* Clear (possibly left over) interrupt bit */ - clear_bit_inv(bit, iv->vector); - /* Make the bit position available again */ - set_bit_inv(bit, iv->avail); - if (bit == iv->end - 1) { + for (i = 0; i < num; i++) { + /* Clear (possibly left over) interrupt bit */ + clear_bit_inv(bit + i, iv->vector); + /* Make the bit positions available again */ + set_bit_inv(bit + i, iv->avail); + } + if (bit + num >= iv->end) { /* Find new end of bit-field */ - while (--iv->end > 0) - if (!test_bit_inv(iv->end - 1, iv->avail)) - break; + while (iv->end > 0 && !test_bit_inv(iv->end - 1, iv->avail)) + iv->end--; } spin_unlock(&iv->lock); } -EXPORT_SYMBOL(airq_iv_free_bit); +EXPORT_SYMBOL(airq_iv_free); /** * airq_iv_scan - scan interrupt vector for non-zero bits diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index 7b29d0be0ca3..1d3661af7bd8 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -173,8 +173,7 @@ static struct css_driver chsc_subchannel_driver = { static int __init chsc_init_dbfs(void) { - chsc_debug_msg_id = debug_register("chsc_msg", 16, 1, - 16 * sizeof(long)); + chsc_debug_msg_id = debug_register("chsc_msg", 8, 1, 4 * sizeof(long)); if (!chsc_debug_msg_id) goto out; debug_register_view(chsc_debug_msg_id, &debug_sprintf_view); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 8ee88c4ebd83..9e058c4657a3 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -18,6 +18,7 @@ #include <linux/device.h> #include <linux/kernel_stat.h> #include <linux/interrupt.h> +#include <linux/irq.h> #include <asm/cio.h> #include <asm/delay.h> #include <asm/irq.h> @@ -28,7 +29,7 @@ #include <asm/chpid.h> #include <asm/airq.h> #include <asm/isc.h> -#include <asm/cputime.h> +#include <linux/cputime.h> #include <asm/fcx.h> #include <asm/nmi.h> #include <asm/crw.h> @@ -54,7 +55,7 @@ debug_info_t *cio_debug_crw_id; */ static int __init cio_debug_init(void) { - cio_debug_msg_id = debug_register("cio_msg", 16, 1, 16 * sizeof(long)); + cio_debug_msg_id = debug_register("cio_msg", 16, 1, 11 * sizeof(long)); if (!cio_debug_msg_id) goto out_unregister; debug_register_view(cio_debug_msg_id, &debug_sprintf_view); @@ -64,7 +65,7 @@ static int __init cio_debug_init(void) goto out_unregister; debug_register_view(cio_debug_trace_id, &debug_hex_ascii_view); debug_set_level(cio_debug_trace_id, 2); - cio_debug_crw_id = debug_register("cio_crw", 16, 1, 16 * sizeof(long)); + cio_debug_crw_id = debug_register("cio_crw", 8, 1, 8 * sizeof(long)); if (!cio_debug_crw_id) goto out_unregister; debug_register_view(cio_debug_crw_id, &debug_sprintf_view); @@ -584,8 +585,6 @@ static irqreturn_t do_cio_interrupt(int irq, void *dummy) return IRQ_HANDLED; } -static struct irq_desc *irq_desc_io; - static struct irqaction io_interrupt = { .name = "IO", .handler = do_cio_interrupt, @@ -596,7 +595,6 @@ void __init init_cio_interrupts(void) irq_set_chip_and_handler(IO_INTERRUPT, &dummy_irq_chip, handle_percpu_irq); setup_irq(IO_INTERRUPT, &io_interrupt); - irq_desc_io = irq_to_desc(IO_INTERRUPT); } #ifdef CONFIG_CCW_CONSOLE @@ -623,7 +621,7 @@ void cio_tsch(struct subchannel *sch) local_bh_disable(); irq_enter(); } - kstat_incr_irqs_this_cpu(IO_INTERRUPT, irq_desc_io); + kstat_incr_irq_this_cpu(IO_INTERRUPT); if (sch->driver && sch->driver->irq) sch->driver->irq(sch); else diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e9d783563cbb..d8d9b5b5cc56 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1571,12 +1571,27 @@ out: return rc; } +static void ccw_device_set_int_class(struct ccw_device *cdev) +{ + struct ccw_driver *cdrv = cdev->drv; + + /* Note: we interpret class 0 in this context as an uninitialized + * field since it translates to a non-I/O interrupt class. */ + if (cdrv->int_class != 0) + cdev->private->int_class = cdrv->int_class; + else + cdev->private->int_class = IRQIO_CIO; +} + #ifdef CONFIG_CCW_CONSOLE -static int ccw_device_console_enable(struct ccw_device *cdev, - struct subchannel *sch) +int __init ccw_device_enable_console(struct ccw_device *cdev) { + struct subchannel *sch = to_subchannel(cdev->dev.parent); int rc; + if (!cdev->drv || !cdev->handler) + return -EINVAL; + io_subchannel_init_fields(sch); rc = cio_commit_config(sch); if (rc) @@ -1609,12 +1624,11 @@ out_unlock: return rc; } -struct ccw_device *ccw_device_probe_console(void) +struct ccw_device * __init ccw_device_create_console(struct ccw_driver *drv) { struct io_subchannel_private *io_priv; struct ccw_device *cdev; struct subchannel *sch; - int ret; sch = cio_probe_console(); if (IS_ERR(sch)) @@ -1631,18 +1645,23 @@ struct ccw_device *ccw_device_probe_console(void) kfree(io_priv); return cdev; } + cdev->drv = drv; set_io_private(sch, io_priv); - ret = ccw_device_console_enable(cdev, sch); - if (ret) { - set_io_private(sch, NULL); - put_device(&sch->dev); - put_device(&cdev->dev); - kfree(io_priv); - return ERR_PTR(ret); - } + ccw_device_set_int_class(cdev); return cdev; } +void __init ccw_device_destroy_console(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct io_subchannel_private *io_priv = to_io_private(sch); + + set_io_private(sch, NULL); + put_device(&sch->dev); + put_device(&cdev->dev); + kfree(io_priv); +} + /** * ccw_device_wait_idle() - busy wait for device to become idle * @cdev: ccw device @@ -1726,15 +1745,8 @@ ccw_device_probe (struct device *dev) int ret; cdev->drv = cdrv; /* to let the driver call _set_online */ - /* Note: we interpret class 0 in this context as an uninitialized - * field since it translates to a non-I/O interrupt class. */ - if (cdrv->int_class != 0) - cdev->private->int_class = cdrv->int_class; - else - cdev->private->int_class = IRQIO_CIO; - + ccw_device_set_int_class(cdev); ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV; - if (ret) { cdev->drv = NULL; cdev->private->int_class = IRQIO_CIO; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index c3a83df07894..a0aff2eb247c 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -33,8 +33,8 @@ struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS] = { /* N P A M L V H */ [QETH_DBF_SETUP] = {"qeth_setup", 8, 1, 8, 5, &debug_hex_ascii_view, NULL}, - [QETH_DBF_MSG] = {"qeth_msg", - 8, 1, 128, 3, &debug_sprintf_view, NULL}, + [QETH_DBF_MSG] = {"qeth_msg", 8, 1, 11 * sizeof(long), 3, + &debug_sprintf_view, NULL}, [QETH_DBF_CTRL] = {"qeth_control", 8, 1, QETH_DBF_CTRL_LEN, 5, &debug_hex_ascii_view, NULL}, }; @@ -1660,7 +1660,6 @@ int qeth_qdio_clear_card(struct qeth_card *card, int use_halt) QDIO_FLAG_CLEANUP_USING_CLEAR); if (rc) QETH_CARD_TEXT_(card, 3, "1err%d", rc); - qdio_free(CARD_DDEV(card)); atomic_set(&card->qdio.state, QETH_QDIO_ALLOCATED); break; case QETH_QDIO_CLEANING: @@ -2605,6 +2604,7 @@ static int qeth_mpc_initialize(struct qeth_card *card) return 0; out_qdio: qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); + qdio_free(CARD_DDEV(card)); return rc; } @@ -4906,9 +4906,11 @@ retry: if (retries < 3) QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n", dev_name(&card->gdev->dev)); + rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); ccw_device_set_offline(CARD_DDEV(card)); ccw_device_set_offline(CARD_WDEV(card)); ccw_device_set_offline(CARD_RDEV(card)); + qdio_free(CARD_DDEV(card)); rc = ccw_device_set_online(CARD_RDEV(card)); if (rc) goto retriable; @@ -4918,7 +4920,6 @@ retry: rc = ccw_device_set_online(CARD_DDEV(card)); if (rc) goto retriable; - rc = qeth_qdio_clear_card(card, card->info.type != QETH_CARD_TYPE_IQD); retriable: if (rc == -ERESTARTSYS) { QETH_DBF_TEXT(SETUP, 2, "break1"); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 0710550093ce..908d82529ee9 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1091,6 +1091,7 @@ out_remove: ccw_device_set_offline(CARD_DDEV(card)); ccw_device_set_offline(CARD_WDEV(card)); ccw_device_set_offline(CARD_RDEV(card)); + qdio_free(CARD_DDEV(card)); if (recover_flag == CARD_STATE_RECOVER) card->state = CARD_STATE_RECOVER; else @@ -1132,6 +1133,7 @@ static int __qeth_l2_set_offline(struct ccwgroup_device *cgdev, rc = (rc2) ? rc2 : rc3; if (rc) QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); + qdio_free(CARD_DDEV(card)); if (recover_flag == CARD_STATE_UP) card->state = CARD_STATE_RECOVER; /* let user_space know that device is offline */ @@ -1194,6 +1196,7 @@ static void qeth_l2_shutdown(struct ccwgroup_device *gdev) qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); qeth_qdio_clear_card(card, 0); qeth_clear_qdio_buffers(card); + qdio_free(CARD_DDEV(card)); } static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 0f430424c3b8..3524d34ff694 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -3447,6 +3447,7 @@ out_remove: ccw_device_set_offline(CARD_DDEV(card)); ccw_device_set_offline(CARD_WDEV(card)); ccw_device_set_offline(CARD_RDEV(card)); + qdio_free(CARD_DDEV(card)); if (recover_flag == CARD_STATE_RECOVER) card->state = CARD_STATE_RECOVER; else @@ -3493,6 +3494,7 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, rc = (rc2) ? rc2 : rc3; if (rc) QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); + qdio_free(CARD_DDEV(card)); if (recover_flag == CARD_STATE_UP) card->state = CARD_STATE_RECOVER; /* let user_space know that device is offline */ @@ -3545,6 +3547,7 @@ static void qeth_l3_shutdown(struct ccwgroup_device *gdev) qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); qeth_qdio_clear_card(card, 0); qeth_clear_qdio_buffers(card); + qdio_free(CARD_DDEV(card)); } static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev) diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c index a3e6c8a3ff0f..296c936cc03c 100644 --- a/drivers/scsi/atari_scsi.c +++ b/drivers/scsi/atari_scsi.c @@ -90,6 +90,7 @@ #include <linux/init.h> #include <linux/nvram.h> #include <linux/bitops.h> +#include <linux/wait.h> #include <asm/setup.h> #include <asm/atarihw.h> @@ -549,8 +550,10 @@ static void falcon_get_lock(void) local_irq_save(flags); - while (!in_irq() && falcon_got_lock && stdma_others_waiting()) - sleep_on(&falcon_fairness_wait); + wait_event_cmd(falcon_fairness_wait, + in_interrupt() || !falcon_got_lock || !stdma_others_waiting(), + local_irq_restore(flags), + local_irq_save(flags)); while (!falcon_got_lock) { if (in_irq()) @@ -562,7 +565,10 @@ static void falcon_get_lock(void) falcon_trying_lock = 0; wake_up(&falcon_try_wait); } else { - sleep_on(&falcon_try_wait); + wait_event_cmd(falcon_try_wait, + falcon_got_lock && !falcon_trying_lock, + local_irq_restore(flags), + local_irq_save(flags)); } } diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 1f375051483a..5642a9b250c2 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -325,7 +325,7 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc) if (!abrt_task->sc || abrt_task->state == ISCSI_TASK_FREE) continue; - if (abrt_task->sc->device->lun != abrt_task->sc->device->lun) + if (sc->device->lun != abrt_task->sc->device->lun) continue; /* Invalidate WRB Posted for this Task */ diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index ed880891cb7c..e9279a8c1e1c 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -594,13 +594,13 @@ static void bnx2fc_free_mp_resc(struct bnx2fc_cmd *io_req) mp_req->mp_resp_bd = NULL; } if (mp_req->req_buf) { - dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, mp_req->req_buf, mp_req->req_buf_dma); mp_req->req_buf = NULL; } if (mp_req->resp_buf) { - dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, mp_req->resp_buf, mp_req->resp_buf_dma); mp_req->resp_buf = NULL; @@ -622,7 +622,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) mp_req->req_len = sizeof(struct fcp_cmnd); io_req->data_xfer_len = mp_req->req_len; - mp_req->req_buf = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE, + mp_req->req_buf = dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, &mp_req->req_buf_dma, GFP_ATOMIC); if (!mp_req->req_buf) { @@ -631,7 +631,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) return FAILED; } - mp_req->resp_buf = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE, + mp_req->resp_buf = dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, &mp_req->resp_buf_dma, GFP_ATOMIC); if (!mp_req->resp_buf) { @@ -639,8 +639,8 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) bnx2fc_free_mp_resc(io_req); return FAILED; } - memset(mp_req->req_buf, 0, PAGE_SIZE); - memset(mp_req->resp_buf, 0, PAGE_SIZE); + memset(mp_req->req_buf, 0, CNIC_PAGE_SIZE); + memset(mp_req->resp_buf, 0, CNIC_PAGE_SIZE); /* Allocate and map mp_req_bd and mp_resp_bd */ sz = sizeof(struct fcoe_bd_ctx); @@ -665,7 +665,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) mp_req_bd = mp_req->mp_req_bd; mp_req_bd->buf_addr_lo = (u32)addr & 0xffffffff; mp_req_bd->buf_addr_hi = (u32)((u64)addr >> 32); - mp_req_bd->buf_len = PAGE_SIZE; + mp_req_bd->buf_len = CNIC_PAGE_SIZE; mp_req_bd->flags = 0; /* @@ -677,7 +677,7 @@ int bnx2fc_init_mp_req(struct bnx2fc_cmd *io_req) addr = mp_req->resp_buf_dma; mp_resp_bd->buf_addr_lo = (u32)addr & 0xffffffff; mp_resp_bd->buf_addr_hi = (u32)((u64)addr >> 32); - mp_resp_bd->buf_len = PAGE_SIZE; + mp_resp_bd->buf_len = CNIC_PAGE_SIZE; mp_resp_bd->flags = 0; return SUCCESS; diff --git a/drivers/scsi/bnx2fc/bnx2fc_tgt.c b/drivers/scsi/bnx2fc/bnx2fc_tgt.c index 4d93177dfb53..d9bae5672273 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_tgt.c +++ b/drivers/scsi/bnx2fc/bnx2fc_tgt.c @@ -673,7 +673,8 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, /* Allocate and map SQ */ tgt->sq_mem_size = tgt->max_sqes * BNX2FC_SQ_WQE_SIZE; - tgt->sq_mem_size = (tgt->sq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + tgt->sq_mem_size = (tgt->sq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->sq = dma_alloc_coherent(&hba->pcidev->dev, tgt->sq_mem_size, &tgt->sq_dma, GFP_KERNEL); @@ -686,7 +687,8 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, /* Allocate and map CQ */ tgt->cq_mem_size = tgt->max_cqes * BNX2FC_CQ_WQE_SIZE; - tgt->cq_mem_size = (tgt->cq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + tgt->cq_mem_size = (tgt->cq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->cq = dma_alloc_coherent(&hba->pcidev->dev, tgt->cq_mem_size, &tgt->cq_dma, GFP_KERNEL); @@ -699,7 +701,8 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, /* Allocate and map RQ and RQ PBL */ tgt->rq_mem_size = tgt->max_rqes * BNX2FC_RQ_WQE_SIZE; - tgt->rq_mem_size = (tgt->rq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + tgt->rq_mem_size = (tgt->rq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->rq = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_mem_size, &tgt->rq_dma, GFP_KERNEL); @@ -710,8 +713,9 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, } memset(tgt->rq, 0, tgt->rq_mem_size); - tgt->rq_pbl_size = (tgt->rq_mem_size / PAGE_SIZE) * sizeof(void *); - tgt->rq_pbl_size = (tgt->rq_pbl_size + (PAGE_SIZE - 1)) & PAGE_MASK; + tgt->rq_pbl_size = (tgt->rq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *); + tgt->rq_pbl_size = (tgt->rq_pbl_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->rq_pbl = dma_alloc_coherent(&hba->pcidev->dev, tgt->rq_pbl_size, &tgt->rq_pbl_dma, GFP_KERNEL); @@ -722,7 +726,7 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, } memset(tgt->rq_pbl, 0, tgt->rq_pbl_size); - num_pages = tgt->rq_mem_size / PAGE_SIZE; + num_pages = tgt->rq_mem_size / CNIC_PAGE_SIZE; page = tgt->rq_dma; pbl = (u32 *)tgt->rq_pbl; @@ -731,13 +735,13 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, pbl++; *pbl = (u32)((u64)page >> 32); pbl++; - page += PAGE_SIZE; + page += CNIC_PAGE_SIZE; } /* Allocate and map XFERQ */ tgt->xferq_mem_size = tgt->max_sqes * BNX2FC_XFERQ_WQE_SIZE; - tgt->xferq_mem_size = (tgt->xferq_mem_size + (PAGE_SIZE - 1)) & - PAGE_MASK; + tgt->xferq_mem_size = (tgt->xferq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->xferq = dma_alloc_coherent(&hba->pcidev->dev, tgt->xferq_mem_size, &tgt->xferq_dma, GFP_KERNEL); @@ -750,8 +754,8 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, /* Allocate and map CONFQ & CONFQ PBL */ tgt->confq_mem_size = tgt->max_sqes * BNX2FC_CONFQ_WQE_SIZE; - tgt->confq_mem_size = (tgt->confq_mem_size + (PAGE_SIZE - 1)) & - PAGE_MASK; + tgt->confq_mem_size = (tgt->confq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->confq = dma_alloc_coherent(&hba->pcidev->dev, tgt->confq_mem_size, &tgt->confq_dma, GFP_KERNEL); @@ -763,9 +767,9 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, memset(tgt->confq, 0, tgt->confq_mem_size); tgt->confq_pbl_size = - (tgt->confq_mem_size / PAGE_SIZE) * sizeof(void *); + (tgt->confq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *); tgt->confq_pbl_size = - (tgt->confq_pbl_size + (PAGE_SIZE - 1)) & PAGE_MASK; + (tgt->confq_pbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; tgt->confq_pbl = dma_alloc_coherent(&hba->pcidev->dev, tgt->confq_pbl_size, @@ -777,7 +781,7 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, } memset(tgt->confq_pbl, 0, tgt->confq_pbl_size); - num_pages = tgt->confq_mem_size / PAGE_SIZE; + num_pages = tgt->confq_mem_size / CNIC_PAGE_SIZE; page = tgt->confq_dma; pbl = (u32 *)tgt->confq_pbl; @@ -786,7 +790,7 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, pbl++; *pbl = (u32)((u64)page >> 32); pbl++; - page += PAGE_SIZE; + page += CNIC_PAGE_SIZE; } /* Allocate and map ConnDB */ @@ -805,8 +809,8 @@ static int bnx2fc_alloc_session_resc(struct bnx2fc_hba *hba, /* Allocate and map LCQ */ tgt->lcq_mem_size = (tgt->max_sqes + 8) * BNX2FC_SQ_WQE_SIZE; - tgt->lcq_mem_size = (tgt->lcq_mem_size + (PAGE_SIZE - 1)) & - PAGE_MASK; + tgt->lcq_mem_size = (tgt->lcq_mem_size + (CNIC_PAGE_SIZE - 1)) & + CNIC_PAGE_MASK; tgt->lcq = dma_alloc_coherent(&hba->pcidev->dev, tgt->lcq_mem_size, &tgt->lcq_dma, GFP_KERNEL); diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index e4cf23df4b4f..b87a1933f880 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -61,7 +61,7 @@ static void bnx2i_adjust_qp_size(struct bnx2i_hba *hba) * yield integral num of page buffers */ /* adjust SQ */ - num_elements_per_pg = PAGE_SIZE / BNX2I_SQ_WQE_SIZE; + num_elements_per_pg = CNIC_PAGE_SIZE / BNX2I_SQ_WQE_SIZE; if (hba->max_sqes < num_elements_per_pg) hba->max_sqes = num_elements_per_pg; else if (hba->max_sqes % num_elements_per_pg) @@ -69,7 +69,7 @@ static void bnx2i_adjust_qp_size(struct bnx2i_hba *hba) ~(num_elements_per_pg - 1); /* adjust CQ */ - num_elements_per_pg = PAGE_SIZE / BNX2I_CQE_SIZE; + num_elements_per_pg = CNIC_PAGE_SIZE / BNX2I_CQE_SIZE; if (hba->max_cqes < num_elements_per_pg) hba->max_cqes = num_elements_per_pg; else if (hba->max_cqes % num_elements_per_pg) @@ -77,7 +77,7 @@ static void bnx2i_adjust_qp_size(struct bnx2i_hba *hba) ~(num_elements_per_pg - 1); /* adjust RQ */ - num_elements_per_pg = PAGE_SIZE / BNX2I_RQ_WQE_SIZE; + num_elements_per_pg = CNIC_PAGE_SIZE / BNX2I_RQ_WQE_SIZE; if (hba->max_rqes < num_elements_per_pg) hba->max_rqes = num_elements_per_pg; else if (hba->max_rqes % num_elements_per_pg) @@ -959,7 +959,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep) /* SQ page table */ memset(ep->qp.sq_pgtbl_virt, 0, ep->qp.sq_pgtbl_size); - num_pages = ep->qp.sq_mem_size / PAGE_SIZE; + num_pages = ep->qp.sq_mem_size / CNIC_PAGE_SIZE; page = ep->qp.sq_phys; if (cnic_dev_10g) @@ -973,7 +973,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep) ptbl++; *ptbl = (u32) ((u64) page >> 32); ptbl++; - page += PAGE_SIZE; + page += CNIC_PAGE_SIZE; } else { /* PTE is written in big endian format for * 5706/5708/5709 devices */ @@ -981,13 +981,13 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep) ptbl++; *ptbl = (u32) page; ptbl++; - page += PAGE_SIZE; + page += CNIC_PAGE_SIZE; } } /* RQ page table */ memset(ep->qp.rq_pgtbl_virt, 0, ep->qp.rq_pgtbl_size); - num_pages = ep->qp.rq_mem_size / PAGE_SIZE; + num_pages = ep->qp.rq_mem_size / CNIC_PAGE_SIZE; page = ep->qp.rq_phys; if (cnic_dev_10g) @@ -1001,7 +1001,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep) ptbl++; *ptbl = (u32) ((u64) page >> 32); ptbl++; - page += PAGE_SIZE; + page += CNIC_PAGE_SIZE; } else { /* PTE is written in big endian format for * 5706/5708/5709 devices */ @@ -1009,13 +1009,13 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep) ptbl++; *ptbl = (u32) page; ptbl++; - page += PAGE_SIZE; + page += CNIC_PAGE_SIZE; } } /* CQ page table */ memset(ep->qp.cq_pgtbl_virt, 0, ep->qp.cq_pgtbl_size); - num_pages = ep->qp.cq_mem_size / PAGE_SIZE; + num_pages = ep->qp.cq_mem_size / CNIC_PAGE_SIZE; page = ep->qp.cq_phys; if (cnic_dev_10g) @@ -1029,7 +1029,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep) ptbl++; *ptbl = (u32) ((u64) page >> 32); ptbl++; - page += PAGE_SIZE; + page += CNIC_PAGE_SIZE; } else { /* PTE is written in big endian format for * 5706/5708/5709 devices */ @@ -1037,7 +1037,7 @@ static void setup_qp_page_tables(struct bnx2i_endpoint *ep) ptbl++; *ptbl = (u32) page; ptbl++; - page += PAGE_SIZE; + page += CNIC_PAGE_SIZE; } } } @@ -1064,11 +1064,11 @@ int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep) /* Allocate page table memory for SQ which is page aligned */ ep->qp.sq_mem_size = hba->max_sqes * BNX2I_SQ_WQE_SIZE; ep->qp.sq_mem_size = - (ep->qp.sq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + (ep->qp.sq_mem_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; ep->qp.sq_pgtbl_size = - (ep->qp.sq_mem_size / PAGE_SIZE) * sizeof(void *); + (ep->qp.sq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *); ep->qp.sq_pgtbl_size = - (ep->qp.sq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK; + (ep->qp.sq_pgtbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; ep->qp.sq_pgtbl_virt = dma_alloc_coherent(&hba->pcidev->dev, ep->qp.sq_pgtbl_size, @@ -1101,11 +1101,11 @@ int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep) /* Allocate page table memory for CQ which is page aligned */ ep->qp.cq_mem_size = hba->max_cqes * BNX2I_CQE_SIZE; ep->qp.cq_mem_size = - (ep->qp.cq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + (ep->qp.cq_mem_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; ep->qp.cq_pgtbl_size = - (ep->qp.cq_mem_size / PAGE_SIZE) * sizeof(void *); + (ep->qp.cq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *); ep->qp.cq_pgtbl_size = - (ep->qp.cq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK; + (ep->qp.cq_pgtbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; ep->qp.cq_pgtbl_virt = dma_alloc_coherent(&hba->pcidev->dev, ep->qp.cq_pgtbl_size, @@ -1144,11 +1144,11 @@ int bnx2i_alloc_qp_resc(struct bnx2i_hba *hba, struct bnx2i_endpoint *ep) /* Allocate page table memory for RQ which is page aligned */ ep->qp.rq_mem_size = hba->max_rqes * BNX2I_RQ_WQE_SIZE; ep->qp.rq_mem_size = - (ep->qp.rq_mem_size + (PAGE_SIZE - 1)) & PAGE_MASK; + (ep->qp.rq_mem_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; ep->qp.rq_pgtbl_size = - (ep->qp.rq_mem_size / PAGE_SIZE) * sizeof(void *); + (ep->qp.rq_mem_size / CNIC_PAGE_SIZE) * sizeof(void *); ep->qp.rq_pgtbl_size = - (ep->qp.rq_pgtbl_size + (PAGE_SIZE - 1)) & PAGE_MASK; + (ep->qp.rq_pgtbl_size + (CNIC_PAGE_SIZE - 1)) & CNIC_PAGE_MASK; ep->qp.rq_pgtbl_virt = dma_alloc_coherent(&hba->pcidev->dev, ep->qp.rq_pgtbl_size, @@ -1270,7 +1270,7 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba) bnx2i_adjust_qp_size(hba); iscsi_init.flags = - ISCSI_PAGE_SIZE_4K << ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT; + (CNIC_PAGE_BITS - 8) << ISCSI_KWQE_INIT1_PAGE_SIZE_SHIFT; if (en_tcp_dack) iscsi_init.flags |= ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE; iscsi_init.reserved0 = 0; @@ -1288,15 +1288,15 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba) ((hba->num_ccell & 0xFFFF) | (hba->max_sqes << 16)); iscsi_init.num_ccells_per_conn = hba->num_ccell; iscsi_init.num_tasks_per_conn = hba->max_sqes; - iscsi_init.sq_wqes_per_page = PAGE_SIZE / BNX2I_SQ_WQE_SIZE; + iscsi_init.sq_wqes_per_page = CNIC_PAGE_SIZE / BNX2I_SQ_WQE_SIZE; iscsi_init.sq_num_wqes = hba->max_sqes; iscsi_init.cq_log_wqes_per_page = - (u8) bnx2i_power_of2(PAGE_SIZE / BNX2I_CQE_SIZE); + (u8) bnx2i_power_of2(CNIC_PAGE_SIZE / BNX2I_CQE_SIZE); iscsi_init.cq_num_wqes = hba->max_cqes; iscsi_init.cq_num_pages = (hba->max_cqes * BNX2I_CQE_SIZE + - (PAGE_SIZE - 1)) / PAGE_SIZE; + (CNIC_PAGE_SIZE - 1)) / CNIC_PAGE_SIZE; iscsi_init.sq_num_pages = (hba->max_sqes * BNX2I_SQ_WQE_SIZE + - (PAGE_SIZE - 1)) / PAGE_SIZE; + (CNIC_PAGE_SIZE - 1)) / CNIC_PAGE_SIZE; iscsi_init.rq_buffer_size = BNX2I_RQ_WQE_SIZE; iscsi_init.rq_num_wqes = hba->max_rqes; diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index 854dad7d5b03..c8b0aff5bbd4 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -525,7 +525,7 @@ static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba) struct iscsi_bd *mp_bdt; u64 addr; - hba->mp_bd_tbl = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE, + hba->mp_bd_tbl = dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, &hba->mp_bd_dma, GFP_KERNEL); if (!hba->mp_bd_tbl) { printk(KERN_ERR "unable to allocate Middle Path BDT\n"); @@ -533,11 +533,12 @@ static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba) goto out; } - hba->dummy_buffer = dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE, + hba->dummy_buffer = dma_alloc_coherent(&hba->pcidev->dev, + CNIC_PAGE_SIZE, &hba->dummy_buf_dma, GFP_KERNEL); if (!hba->dummy_buffer) { printk(KERN_ERR "unable to alloc Middle Path Dummy Buffer\n"); - dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, hba->mp_bd_tbl, hba->mp_bd_dma); hba->mp_bd_tbl = NULL; rc = -1; @@ -548,7 +549,7 @@ static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba) addr = (unsigned long) hba->dummy_buf_dma; mp_bdt->buffer_addr_lo = addr & 0xffffffff; mp_bdt->buffer_addr_hi = addr >> 32; - mp_bdt->buffer_length = PAGE_SIZE; + mp_bdt->buffer_length = CNIC_PAGE_SIZE; mp_bdt->flags = ISCSI_BD_LAST_IN_BD_CHAIN | ISCSI_BD_FIRST_IN_BD_CHAIN; out: @@ -565,12 +566,12 @@ out: static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba) { if (hba->mp_bd_tbl) { - dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, hba->mp_bd_tbl, hba->mp_bd_dma); hba->mp_bd_tbl = NULL; } if (hba->dummy_buffer) { - dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, hba->dummy_buffer, hba->dummy_buf_dma); hba->dummy_buffer = NULL; } @@ -934,14 +935,14 @@ static void bnx2i_conn_free_login_resources(struct bnx2i_hba *hba, struct bnx2i_conn *bnx2i_conn) { if (bnx2i_conn->gen_pdu.resp_bd_tbl) { - dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, bnx2i_conn->gen_pdu.resp_bd_tbl, bnx2i_conn->gen_pdu.resp_bd_dma); bnx2i_conn->gen_pdu.resp_bd_tbl = NULL; } if (bnx2i_conn->gen_pdu.req_bd_tbl) { - dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, bnx2i_conn->gen_pdu.req_bd_tbl, bnx2i_conn->gen_pdu.req_bd_dma); bnx2i_conn->gen_pdu.req_bd_tbl = NULL; @@ -998,13 +999,13 @@ static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba, bnx2i_conn->gen_pdu.resp_wr_ptr = bnx2i_conn->gen_pdu.resp_buf; bnx2i_conn->gen_pdu.req_bd_tbl = - dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, &bnx2i_conn->gen_pdu.req_bd_dma, GFP_KERNEL); if (bnx2i_conn->gen_pdu.req_bd_tbl == NULL) goto login_req_bd_tbl_failure; bnx2i_conn->gen_pdu.resp_bd_tbl = - dma_alloc_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_alloc_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, &bnx2i_conn->gen_pdu.resp_bd_dma, GFP_KERNEL); if (bnx2i_conn->gen_pdu.resp_bd_tbl == NULL) @@ -1013,7 +1014,7 @@ static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba, return 0; login_resp_bd_tbl_failure: - dma_free_coherent(&hba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&hba->pcidev->dev, CNIC_PAGE_SIZE, bnx2i_conn->gen_pdu.req_bd_tbl, bnx2i_conn->gen_pdu.req_bd_dma); bnx2i_conn->gen_pdu.req_bd_tbl = NULL; diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 4911310a38f5..22a9bb1abae1 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -311,9 +311,8 @@ static inline struct Scsi_Host *to_shost(struct isci_host *ihost) } #define for_each_isci_host(id, ihost, pdev) \ - for (id = 0, ihost = to_pci_info(pdev)->hosts[id]; \ - id < ARRAY_SIZE(to_pci_info(pdev)->hosts) && ihost; \ - ihost = to_pci_info(pdev)->hosts[++id]) + for (id = 0; id < SCI_MAX_CONTROLLERS && \ + (ihost = to_pci_info(pdev)->hosts[id]); id++) static inline void wait_for_start(struct isci_host *ihost) { diff --git a/drivers/scsi/isci/port_config.c b/drivers/scsi/isci/port_config.c index 85c77f6b802b..ac879745ef80 100644 --- a/drivers/scsi/isci/port_config.c +++ b/drivers/scsi/isci/port_config.c @@ -615,13 +615,6 @@ static void sci_apc_agent_link_up(struct isci_host *ihost, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION); } else { /* the phy is already the part of the port */ - u32 port_state = iport->sm.current_state_id; - - /* if the PORT'S state is resetting then the link up is from - * port hard reset in this case, we need to tell the port - * that link up is recieved - */ - BUG_ON(port_state != SCI_PORT_RESETTING); port_agent->phy_ready_mask |= 1 << phy_index; sci_port_link_up(iport, iphy); } diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 0d30ca849e8f..5d6fda72d659 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c @@ -801,7 +801,7 @@ int isci_task_I_T_nexus_reset(struct domain_device *dev) /* XXX: need to cleanup any ireqs targeting this * domain_device */ - ret = TMF_RESP_FUNC_COMPLETE; + ret = -ENODEV; goto out; } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index d2895836f9fa..766098af4eb7 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -700,46 +700,26 @@ void sas_probe_sata(struct asd_sas_port *port) } -static bool sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func) +static void sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func) { struct domain_device *dev, *n; - bool retry = false; list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) { - int rc; - if (!dev_is_sata(dev)) continue; sas_ata_wait_eh(dev); - rc = dev->sata_dev.pm_result; - if (rc == -EAGAIN) - retry = true; - else if (rc) { - /* since we don't have a - * ->port_{suspend|resume} routine in our - * ata_port ops, and no entanglements with - * acpi, suspend should just be mechanical trip - * through eh, catch cases where these - * assumptions are invalidated - */ - WARN_ONCE(1, "failed %s %s error: %d\n", func, - dev_name(&dev->rphy->dev), rc); - } /* if libata failed to power manage the device, tear it down */ if (ata_dev_disabled(sas_to_ata_dev(dev))) sas_fail_probe(dev, func, -ENODEV); } - - return retry; } void sas_suspend_sata(struct asd_sas_port *port) { struct domain_device *dev; - retry: mutex_lock(&port->ha->disco_mutex); list_for_each_entry(dev, &port->dev_list, dev_list_node) { struct sata_device *sata; @@ -751,20 +731,17 @@ void sas_suspend_sata(struct asd_sas_port *port) if (sata->ap->pm_mesg.event == PM_EVENT_SUSPEND) continue; - sata->pm_result = -EIO; - ata_sas_port_async_suspend(sata->ap, &sata->pm_result); + ata_sas_port_suspend(sata->ap); } mutex_unlock(&port->ha->disco_mutex); - if (sas_ata_flush_pm_eh(port, __func__)) - goto retry; + sas_ata_flush_pm_eh(port, __func__); } void sas_resume_sata(struct asd_sas_port *port) { struct domain_device *dev; - retry: mutex_lock(&port->ha->disco_mutex); list_for_each_entry(dev, &port->dev_list, dev_list_node) { struct sata_device *sata; @@ -776,13 +753,11 @@ void sas_resume_sata(struct asd_sas_port *port) if (sata->ap->pm_mesg.event == PM_EVENT_ON) continue; - sata->pm_result = -EIO; - ata_sas_port_async_resume(sata->ap, &sata->pm_result); + ata_sas_port_resume(sata->ap); } mutex_unlock(&port->ha->disco_mutex); - if (sas_ata_flush_pm_eh(port, __func__)) - goto retry; + sas_ata_flush_pm_eh(port, __func__); } /** diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index e1fe95ef23e1..266724b6b899 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2996,8 +2996,7 @@ struct qla_hw_data { IS_QLA82XX(ha) || IS_QLA83XX(ha) || \ IS_QLA8044(ha)) #define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha)) -#define IS_NOPOLLING_TYPE(ha) ((IS_QLA25XX(ha) || IS_QLA81XX(ha) || \ - IS_QLA83XX(ha)) && (ha)->flags.msix_enabled) +#define IS_NOPOLLING_TYPE(ha) (IS_QLA81XX(ha) && (ha)->flags.msix_enabled) #define IS_FAC_REQUIRED(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha)) #define IS_NOCACHE_VPD_TYPE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha)) #define IS_ALOGIO_CAPABLE(ha) (IS_QLA23XX(ha) || IS_FWI2_CAPABLE(ha)) diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 9bc86b9e86b1..0a1dcb43d18b 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -2880,6 +2880,7 @@ static int qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) { #define MIN_MSIX_COUNT 2 +#define ATIO_VECTOR 2 int i, ret; struct msix_entry *entries; struct qla_msix_entry *qentry; @@ -2936,34 +2937,47 @@ msix_failed: } /* Enable MSI-X vectors for the base queue */ - for (i = 0; i < ha->msix_count; i++) { + for (i = 0; i < 2; i++) { qentry = &ha->msix_entries[i]; - if (QLA_TGT_MODE_ENABLED() && IS_ATIO_MSIX_CAPABLE(ha)) { - ret = request_irq(qentry->vector, - qla83xx_msix_entries[i].handler, - 0, qla83xx_msix_entries[i].name, rsp); - } else if (IS_P3P_TYPE(ha)) { + if (IS_P3P_TYPE(ha)) ret = request_irq(qentry->vector, qla82xx_msix_entries[i].handler, 0, qla82xx_msix_entries[i].name, rsp); - } else { + else ret = request_irq(qentry->vector, msix_entries[i].handler, 0, msix_entries[i].name, rsp); - } - if (ret) { - ql_log(ql_log_fatal, vha, 0x00cb, - "MSI-X: unable to register handler -- %x/%d.\n", - qentry->vector, ret); - qla24xx_disable_msix(ha); - ha->mqenable = 0; - goto msix_out; - } + if (ret) + goto msix_register_fail; qentry->have_irq = 1; qentry->rsp = rsp; rsp->msix = qentry; } + /* + * If target mode is enable, also request the vector for the ATIO + * queue. + */ + if (QLA_TGT_MODE_ENABLED() && IS_ATIO_MSIX_CAPABLE(ha)) { + qentry = &ha->msix_entries[ATIO_VECTOR]; + ret = request_irq(qentry->vector, + qla83xx_msix_entries[ATIO_VECTOR].handler, + 0, qla83xx_msix_entries[ATIO_VECTOR].name, rsp); + qentry->have_irq = 1; + qentry->rsp = rsp; + rsp->msix = qentry; + } + +msix_register_fail: + if (ret) { + ql_log(ql_log_fatal, vha, 0x00cb, + "MSI-X: unable to register handler -- %x/%d.\n", + qentry->vector, ret); + qla24xx_disable_msix(ha); + ha->mqenable = 0; + goto msix_out; + } + /* Enable MSI-X vector for response queue update for queue 0 */ if (IS_QLA83XX(ha)) { if (ha->msixbase && ha->mqiobase && diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 17d740427240..9969fa1ef7c4 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1419,6 +1419,9 @@ static void storvsc_device_destroy(struct scsi_device *sdevice) { struct stor_mem_pools *memp = sdevice->hostdata; + if (!memp) + return; + mempool_destroy(memp->request_mempool); kmem_cache_destroy(memp->request_pool); kfree(memp); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 581ee2a8856b..efe1960af2b3 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -150,7 +150,7 @@ config SPI_BUTTERFLY config SPI_CLPS711X tristate "CLPS711X host SPI controller" - depends on ARCH_CLPS711X + depends on ARCH_CLPS711X || COMPILE_TEST help This enables dedicated general purpose SPI/Microwire1-compatible master mode interface (SSI1) for CLPS711X-based CPUs. @@ -212,7 +212,6 @@ config SPI_IMX tristate "Freescale i.MX SPI controllers" depends on ARCH_MXC || COMPILE_TEST select SPI_BITBANG - default m if IMX_HAVE_PLATFORM_SPI_IMX help This enables using the Freescale i.MX SPI controllers in master mode. @@ -270,6 +269,7 @@ 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 This enables support for the Freescale DSPI controller in master @@ -307,7 +307,7 @@ config SPI_OMAP_UWIRE config SPI_OMAP24XX tristate "McSPI driver for OMAP" - depends on ARM || ARM64 || AVR32 || HEXAGON || MIPS || SH + depends on ARM || ARM64 || AVR32 || HEXAGON || MIPS || SUPERH depends on ARCH_OMAP2PLUS || COMPILE_TEST help SPI master controller for OMAP24XX and later Multichannel SPI @@ -381,6 +381,19 @@ config SPI_RSPI help SPI driver for Renesas RSPI and QSPI blocks. +config SPI_QUP + tristate "Qualcomm SPI controller with QUP interface" + depends on ARCH_MSM_DT || (ARM && COMPILE_TEST) + help + Qualcomm Universal Peripheral (QUP) core is an AHB slave that + provides a common data path (an output FIFO and an input FIFO) + for serial peripheral interface (SPI) mini-core. SPI in master + mode supports up to 50MHz, up to four chip selects, programmable + data path from 4 bits to 32 bits and numerous protocol variants. + + This driver can also be built as a module. If so, the module + will be called spi_qup. + config SPI_S3C24XX tristate "Samsung S3C24XX series SPI" depends on ARCH_S3C24XX @@ -416,7 +429,6 @@ config SPI_SH_MSIOF tristate "SuperH MSIOF SPI controller" depends on HAVE_CLK depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST - select SPI_BITBANG help SPI driver for SuperH and SH Mobile MSIOF blocks. @@ -446,6 +458,19 @@ config SPI_SIRF help SPI driver for CSR SiRFprimaII SoCs +config SPI_SUN4I + tristate "Allwinner A10 SoCs SPI controller" + depends on ARCH_SUNXI || COMPILE_TEST + help + SPI driver for Allwinner sun4i, sun5i and sun7i SoCs + +config SPI_SUN6I + tristate "Allwinner A31 SPI controller" + depends on ARCH_SUNXI || COMPILE_TEST + depends on RESET_CONTROLLER + help + This enables using the SPI controller on the Allwinner A31 SoCs. + config SPI_MXS tristate "Freescale MXS SPI controller" depends on ARCH_MXS @@ -478,13 +503,6 @@ config SPI_TEGRA20_SLINK help SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface. -config SPI_TI_SSP - tristate "TI Sequencer Serial Port - SPI Support" - depends on MFD_TI_SSP - help - This selects an SPI master implementation using a TI sequencer - serial port. - config SPI_TOPCLIFF_PCH tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI" depends on PCI @@ -520,6 +538,19 @@ config SPI_XILINX Or for the DS570, see "XPS Serial Peripheral Interface (SPI) (v2.00b)" +config SPI_XTENSA_XTFPGA + tristate "Xtensa SPI controller for xtfpga" + depends on (XTENSA && XTENSA_PLATFORM_XTFPGA) || COMPILE_TEST + select SPI_BITBANG + help + SPI driver for xtfpga SPI master controller. + + This simple SPI master controller is built into xtfpga bitstreams + and is used to control daughterboard audio codec. It always transfers + 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 @@ -546,7 +577,7 @@ config SPI_DW_MID_DMA config SPI_DW_MMIO tristate "Memory-mapped io interface driver for DW SPI core" - depends on SPI_DESIGNWARE && HAVE_CLK + depends on SPI_DESIGNWARE # # There are lots of SPI device types, with sensors and memory diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 95af48d2d360..bd792669e563 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -59,6 +59,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o +obj-$(CONFIG_SPI_QUP) += spi-qup.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o spi-s3c24xx-hw-y := spi-s3c24xx.o @@ -70,12 +71,14 @@ 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_SUN4I) += spi-sun4i.o +obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o -obj-$(CONFIG_SPI_TI_SSP) += spi-ti-ssp.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o +obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index 5d7deaf62867..5b5709a5c957 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -13,7 +13,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/errno.h> #include <linux/module.h> @@ -200,7 +199,6 @@ static irqreturn_t altera_spi_irq(int irq, void *dev) static int altera_spi_probe(struct platform_device *pdev) { - struct altera_spi_platform_data *platp = dev_get_platdata(&pdev->dev); struct altera_spi *hw; struct spi_master *master; struct resource *res; @@ -214,6 +212,8 @@ static int altera_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = 16; master->mode_bits = SPI_CS_HIGH; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); + master->dev.of_node = pdev->dev.of_node; hw = spi_master_get_devdata(master); platform_set_drvdata(pdev, hw); @@ -245,9 +245,6 @@ static int altera_spi_probe(struct platform_device *pdev) if (err) goto exit; } - /* find platform data */ - if (!platp) - hw->bitbang.master->dev.of_node = pdev->dev.of_node; /* register our spi controller */ err = spi_bitbang_start(&hw->bitbang); diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 31534b51715a..3898b0b9ee77 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -14,7 +14,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/workqueue.h> @@ -132,9 +131,9 @@ static int ath79_spi_setup_cs(struct spi_device *spi) flags = GPIOF_DIR_OUT; if (spi->mode & SPI_CS_HIGH) - flags |= GPIOF_INIT_HIGH; - else flags |= GPIOF_INIT_LOW; + else + flags |= GPIOF_INIT_HIGH; status = gpio_request_one(cdata->gpio, flags, dev_name(&spi->dev)); diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index b0842f751016..8005f9869481 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -9,7 +9,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/clk.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -26,6 +25,7 @@ #include <linux/io.h> #include <linux/gpio.h> +#include <linux/pinctrl/consumer.h> /* SPI register offsets */ #define SPI_CR 0x0000 @@ -993,13 +993,6 @@ static int atmel_spi_setup(struct spi_device *spi) as = spi_master_get_devdata(spi->master); - if (spi->chip_select > spi->master->num_chipselect) { - dev_dbg(&spi->dev, - "setup: invalid chipselect %u (%u defined)\n", - spi->chip_select, spi->master->num_chipselect); - return -EINVAL; - } - /* see notes above re chipselect */ if (!atmel_spi_is_v2(as) && spi->chip_select == 0 @@ -1087,14 +1080,6 @@ static int atmel_spi_one_transfer(struct spi_master *master, } } - if (xfer->bits_per_word > 8) { - if (xfer->len % 2) { - dev_dbg(&spi->dev, - "buffer len should be 16 bits aligned\n"); - return -EINVAL; - } - } - /* * DMA map early, for performance (empties dcache ASAP) and * better fault reporting. @@ -1221,9 +1206,6 @@ static int atmel_spi_transfer_one_message(struct spi_master *master, dev_dbg(&spi->dev, "new message %p submitted for %s\n", msg, dev_name(&spi->dev)); - if (unlikely(list_empty(&msg->transfers))) - return -EINVAL; - atmel_spi_lock(as); cs_activate(as, spi); @@ -1244,10 +1226,10 @@ static int atmel_spi_transfer_one_message(struct spi_master *master, list_for_each_entry(xfer, &msg->transfers, transfer_list) { dev_dbg(&spi->dev, - " xfer %p: len %u tx %p/%08x rx %p/%08x\n", + " xfer %p: len %u tx %p/%pad rx %p/%pad\n", xfer, xfer->len, - xfer->tx_buf, xfer->tx_dma, - xfer->rx_buf, xfer->rx_dma); + xfer->tx_buf, &xfer->tx_dma, + xfer->rx_buf, &xfer->rx_dma); } msg_done: @@ -1303,6 +1285,9 @@ static int atmel_spi_probe(struct platform_device *pdev) struct spi_master *master; struct atmel_spi *as; + /* Select default pin state */ + pinctrl_pm_select_default_state(&pdev->dev); + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) return -ENXIO; @@ -1455,8 +1440,19 @@ static int atmel_spi_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct atmel_spi *as = spi_master_get_devdata(master); + int ret; + + /* Stop the queue running */ + ret = spi_master_suspend(master); + if (ret) { + dev_warn(dev, "cannot suspend master\n"); + return ret; + } clk_disable_unprepare(as->clk); + + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -1464,9 +1460,18 @@ static int atmel_spi_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct atmel_spi *as = spi_master_get_devdata(master); + int ret; + + pinctrl_pm_select_default_state(dev); clk_prepare_enable(as->clk); - return 0; + + /* Start the queue running */ + ret = spi_master_resume(master); + if (ret) + dev_err(dev, "problem starting queue (%d)\n", ret); + + return ret; } static SIMPLE_DEV_PM_OPS(atmel_spi_pm_ops, atmel_spi_suspend, atmel_spi_resume); diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index c4141c92bcff..aafb812d7ea6 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -55,8 +55,6 @@ struct au1550_spi { volatile psc_spi_t __iomem *regs; int irq; - unsigned freq_max; - unsigned freq_min; unsigned len; unsigned tx_count; @@ -248,11 +246,8 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) hz = t->speed_hz; } - if (hz > spi->max_speed_hz || hz > hw->freq_max || hz < hw->freq_min) { - dev_err(&spi->dev, "setupxfer: clock rate=%d out of range\n", - hz); + if (!hz) return -EINVAL; - } au1550_spi_bits_handlers_set(hw, spi->bits_per_word); @@ -287,23 +282,6 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) return 0; } -static int au1550_spi_setup(struct spi_device *spi) -{ - struct au1550_spi *hw = spi_master_get_devdata(spi->master); - - if (spi->max_speed_hz == 0) - spi->max_speed_hz = hw->freq_max; - if (spi->max_speed_hz > hw->freq_max - || spi->max_speed_hz < hw->freq_min) - return -EINVAL; - /* - * NOTE: cannot change speed and other hw settings immediately, - * otherwise sharing of spi bus is not possible, - * so do not call setupxfer(spi, NULL) here - */ - return 0; -} - /* * for dma spi transfers, we have to setup rx channel, otherwise there is * no reliable way how to recognize that spi transfer is done @@ -838,7 +816,6 @@ static int au1550_spi_probe(struct platform_device *pdev) hw->bitbang.master = hw->master; hw->bitbang.setup_transfer = au1550_spi_setupxfer; hw->bitbang.chipselect = au1550_spi_chipsel; - hw->bitbang.master->setup = au1550_spi_setup; hw->bitbang.txrx_bufs = au1550_spi_txrx_bufs; if (hw->usedma) { @@ -909,8 +886,9 @@ static int au1550_spi_probe(struct platform_device *pdev) { int min_div = (2 << 0) * (2 * (4 + 1)); int max_div = (2 << 3) * (2 * (63 + 1)); - hw->freq_max = hw->pdata->mainclk_hz / min_div; - hw->freq_min = hw->pdata->mainclk_hz / (max_div + 1) + 1; + master->max_speed_hz = hw->pdata->mainclk_hz / min_div; + master->min_speed_hz = + hw->pdata->mainclk_hz / (max_div + 1) + 1; } au1550_spi_setup_psc_as_spi(hw); diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 8a89dd1f2654..69167456ec1e 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -315,7 +315,6 @@ static int bcm2835_spi_probe(struct platform_device *pdev) master->mode_bits = BCM2835_SPI_MODE_BITS; master->bits_per_word_mask = SPI_BPW_MASK(8); - master->bus_num = -1; master->num_chipselect = 3; master->transfer_one_message = bcm2835_spi_transfer_one; master->dev.of_node = pdev->dev.of_node; diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index b528f9fc8bc0..5a211e98383b 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -180,7 +180,7 @@ static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t) while (pending > 0) { int curr_step = min_t(int, step_size, pending); - init_completion(&bs->done); + reinit_completion(&bs->done); if (tx) { memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, tx, curr_step); tx += curr_step; @@ -369,6 +369,7 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev) bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0)); mutex_init(&bs->bus_mutex); + init_completion(&bs->done); master->bus_num = HSSPI_BUS_NUM; master->num_chipselect = 8; @@ -453,9 +454,8 @@ static int bcm63xx_hsspi_resume(struct device *dev) } #endif -static const struct dev_pm_ops bcm63xx_hsspi_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(bcm63xx_hsspi_suspend, bcm63xx_hsspi_resume) -}; +static SIMPLE_DEV_PM_OPS(bcm63xx_hsspi_pm_ops, bcm63xx_hsspi_suspend, + bcm63xx_hsspi_resume); static struct platform_driver bcm63xx_hsspi_driver = { .driver = { diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 77286aef2adf..0250fa721cea 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -20,7 +20,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> @@ -35,8 +34,6 @@ #include <bcm63xx_dev_spi.h> -#define PFX KBUILD_MODNAME - #define BCM63XX_SPI_MAX_PREPEND 15 struct bcm63xx_spi { @@ -169,7 +166,7 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, transfer_list); } - init_completion(&bs->done); + reinit_completion(&bs->done); /* Fill in the Message control register */ msg_ctl = (len << SPI_BYTE_CNT_SHIFT); @@ -353,6 +350,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) } bs = spi_master_get_devdata(master); + init_completion(&bs->done); platform_set_drvdata(pdev, master); bs->pdev = pdev; diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index 38941e5920b5..f515c5e9db57 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -8,7 +8,6 @@ * Licensed under the GPL-2 or later. */ -#include <linux/init.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/drivers/spi/spi-bfin-v3.c b/drivers/spi/spi-bfin-v3.c index 8f8598834b30..4089d0e0d84e 100644 --- a/drivers/spi/spi-bfin-v3.c +++ b/drivers/spi/spi-bfin-v3.c @@ -822,7 +822,8 @@ static int bfin_spi_probe(struct platform_device *pdev) master->cleanup = bfin_spi_cleanup; master->setup = bfin_spi_setup; master->transfer_one_message = bfin_spi_transfer_one_message; - master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1); + master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | + SPI_BPW_MASK(8); drv_data = spi_master_get_devdata(master); drv_data->master = master; diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index f0f195af75d4..55e57c3eb9bd 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -350,7 +350,6 @@ static void *bfin_spi_next_transfer(struct bfin_spi_master_data *drv_data) static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data) { struct bfin_spi_slave_data *chip = drv_data->cur_chip; - struct spi_transfer *last_transfer; unsigned long flags; struct spi_message *msg; @@ -362,9 +361,6 @@ static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data) queue_work(drv_data->workqueue, &drv_data->pump_messages); spin_unlock_irqrestore(&drv_data->lock, flags); - last_transfer = list_entry(msg->transfers.prev, - struct spi_transfer, transfer_list); - msg->state = NULL; if (!drv_data->cs_change) @@ -1030,10 +1026,6 @@ static int bfin_spi_setup(struct spi_device *spi) } /* translate common spi framework into our register */ - if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) { - dev_err(&spi->dev, "unsupported spi modes detected\n"); - goto error; - } if (spi->mode & SPI_CPOL) chip->ctl_reg |= BIT_CTL_CPOL; if (spi->mode & SPI_CPHA) diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index bd222f6b677d..dc7d2c2d643e 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -16,7 +16,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <linux/init.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/interrupt.h> @@ -467,11 +466,9 @@ EXPORT_SYMBOL_GPL(spi_bitbang_start); /** * spi_bitbang_stop - stops the task providing spi communication */ -int spi_bitbang_stop(struct spi_bitbang *bitbang) +void spi_bitbang_stop(struct spi_bitbang *bitbang) { spi_unregister_master(bitbang->master); - - return 0; } EXPORT_SYMBOL_GPL(spi_bitbang_stop); diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 8081f96bd1d5..ee4f91ccd8fd 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -309,7 +309,6 @@ done: static void butterfly_detach(struct parport *p) { struct butterfly *pp; - int status; /* FIXME this global is ugly ... but, how to quickly get from * the parport to the "struct butterfly" associated with it? @@ -321,7 +320,7 @@ static void butterfly_detach(struct parport *p) butterfly = NULL; /* stop() unregisters child devices too */ - status = spi_bitbang_stop(&pp->bitbang); + spi_bitbang_stop(&pp->bitbang); /* turn off VCC */ parport_write_data(pp->port, 0); diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index 374ba4a48a9e..4cd62f636547 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -11,158 +11,125 @@ #include <linux/io.h> #include <linux/clk.h> -#include <linux/init.h> #include <linux/gpio.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/clps711x.h> #include <linux/spi/spi.h> #include <linux/platform_data/spi-clps711x.h> -#include <mach/hardware.h> - #define DRIVER_NAME "spi-clps711x" -struct spi_clps711x_data { - struct completion done; +#define SYNCIO_FRMLEN(x) ((x) << 8) +#define SYNCIO_TXFRMEN (1 << 14) +struct spi_clps711x_data { + void __iomem *syncio; + struct regmap *syscon; + struct regmap *syscon1; struct clk *spi_clk; - u32 max_speed_hz; u8 *tx_buf; u8 *rx_buf; - int count; + unsigned int bpw; int len; - - int chipselect[0]; }; static int spi_clps711x_setup(struct spi_device *spi) { - struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master); - /* We are expect that SPI-device is not selected */ - gpio_direction_output(hw->chipselect[spi->chip_select], - !(spi->mode & SPI_CS_HIGH)); + gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); return 0; } -static void spi_clps711x_setup_mode(struct spi_device *spi) -{ - /* Setup edge for transfer */ - if (spi->mode & SPI_CPHA) - clps_writew(clps_readw(SYSCON3) | SYSCON3_ADCCKNSEN, SYSCON3); - else - clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCKNSEN, SYSCON3); -} - -static int spi_clps711x_setup_xfer(struct spi_device *spi, - struct spi_transfer *xfer) +static void spi_clps711x_setup_xfer(struct spi_device *spi, + struct spi_transfer *xfer) { - u32 speed = xfer->speed_hz ? : spi->max_speed_hz; - u8 bpw = xfer->bits_per_word; - struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master); - - if (bpw != 8) { - dev_err(&spi->dev, "Unsupported master bus width %i\n", bpw); - return -EINVAL; - } + struct spi_master *master = spi->master; + struct spi_clps711x_data *hw = spi_master_get_devdata(master); /* Setup SPI frequency divider */ - if (!speed || (speed >= hw->max_speed_hz)) - clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) | - SYSCON1_ADCKSEL(3), SYSCON1); - else if (speed >= (hw->max_speed_hz / 2)) - clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) | - SYSCON1_ADCKSEL(2), SYSCON1); - else if (speed >= (hw->max_speed_hz / 8)) - clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) | - SYSCON1_ADCKSEL(1), SYSCON1); + if (xfer->speed_hz >= master->max_speed_hz) + regmap_update_bits(hw->syscon1, SYSCON_OFFSET, + SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(3)); + else if (xfer->speed_hz >= (master->max_speed_hz / 2)) + regmap_update_bits(hw->syscon1, SYSCON_OFFSET, + SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(2)); + else if (xfer->speed_hz >= (master->max_speed_hz / 8)) + regmap_update_bits(hw->syscon1, SYSCON_OFFSET, + SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(1)); else - clps_writel((clps_readl(SYSCON1) & ~SYSCON1_ADCKSEL_MASK) | - SYSCON1_ADCKSEL(0), SYSCON1); - - return 0; + regmap_update_bits(hw->syscon1, SYSCON_OFFSET, + SYSCON1_ADCKSEL_MASK, SYSCON1_ADCKSEL(0)); } -static int spi_clps711x_transfer_one_message(struct spi_master *master, - struct spi_message *msg) +static int spi_clps711x_prepare_message(struct spi_master *master, + struct spi_message *msg) { struct spi_clps711x_data *hw = spi_master_get_devdata(master); - struct spi_transfer *xfer; - int status = 0, cs = hw->chipselect[msg->spi->chip_select]; - u32 data; - - spi_clps711x_setup_mode(msg->spi); - - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - if (spi_clps711x_setup_xfer(msg->spi, xfer)) { - status = -EINVAL; - goto out_xfr; - } + struct spi_device *spi = msg->spi; - gpio_set_value(cs, !!(msg->spi->mode & SPI_CS_HIGH)); - - reinit_completion(&hw->done); - - hw->count = 0; - hw->len = xfer->len; - hw->tx_buf = (u8 *)xfer->tx_buf; - hw->rx_buf = (u8 *)xfer->rx_buf; - - /* Initiate transfer */ - data = hw->tx_buf ? hw->tx_buf[hw->count] : 0; - clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO); - - wait_for_completion(&hw->done); + /* Setup mode for transfer */ + return regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCKNSEN, + (spi->mode & SPI_CPHA) ? + SYSCON3_ADCCKNSEN : 0); +} - if (xfer->delay_usecs) - udelay(xfer->delay_usecs); +static int spi_clps711x_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct spi_clps711x_data *hw = spi_master_get_devdata(master); + u8 data; - if (xfer->cs_change || - list_is_last(&xfer->transfer_list, &msg->transfers)) - gpio_set_value(cs, !(msg->spi->mode & SPI_CS_HIGH)); + spi_clps711x_setup_xfer(spi, xfer); - msg->actual_length += xfer->len; - } + hw->len = xfer->len; + hw->bpw = xfer->bits_per_word; + hw->tx_buf = (u8 *)xfer->tx_buf; + hw->rx_buf = (u8 *)xfer->rx_buf; -out_xfr: - msg->status = status; - spi_finalize_current_message(master); + /* Initiate transfer */ + data = hw->tx_buf ? *hw->tx_buf++ : 0; + writel(data | SYNCIO_FRMLEN(hw->bpw) | SYNCIO_TXFRMEN, hw->syncio); - return 0; + return 1; } static irqreturn_t spi_clps711x_isr(int irq, void *dev_id) { - struct spi_clps711x_data *hw = (struct spi_clps711x_data *)dev_id; - u32 data; + struct spi_master *master = dev_id; + struct spi_clps711x_data *hw = spi_master_get_devdata(master); + u8 data; /* Handle RX */ - data = clps_readb(SYNCIO); + data = readb(hw->syncio); if (hw->rx_buf) - hw->rx_buf[hw->count] = (u8)data; - - hw->count++; + *hw->rx_buf++ = data; /* Handle TX */ - if (hw->count < hw->len) { - data = hw->tx_buf ? hw->tx_buf[hw->count] : 0; - clps_writel(data | SYNCIO_FRMLEN(8) | SYNCIO_TXFRMEN, SYNCIO); + if (--hw->len > 0) { + data = hw->tx_buf ? *hw->tx_buf++ : 0; + writel(data | SYNCIO_FRMLEN(hw->bpw) | SYNCIO_TXFRMEN, + hw->syncio); } else - complete(&hw->done); + spi_finalize_current_transfer(master); return IRQ_HANDLED; } static int spi_clps711x_probe(struct platform_device *pdev) { - int i, ret; - struct spi_master *master; struct spi_clps711x_data *hw; struct spi_clps711x_pdata *pdata = dev_get_platdata(&pdev->dev); + struct spi_master *master; + struct resource *res; + int i, irq, ret; if (!pdata) { dev_err(&pdev->dev, "No platform data supplied\n"); @@ -174,33 +141,37 @@ static int spi_clps711x_probe(struct platform_device *pdev) return -EINVAL; } - master = spi_alloc_master(&pdev->dev, - sizeof(struct spi_clps711x_data) + - sizeof(int) * pdata->num_chipselect); - if (!master) { - dev_err(&pdev->dev, "SPI allocating memory error\n"); + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + master = spi_alloc_master(&pdev->dev, sizeof(*hw)); + if (!master) return -ENOMEM; + + master->cs_gpios = devm_kzalloc(&pdev->dev, sizeof(int) * + pdata->num_chipselect, GFP_KERNEL); + if (!master->cs_gpios) { + ret = -ENOMEM; + goto err_out; } master->bus_num = pdev->id; master->mode_bits = SPI_CPHA | SPI_CS_HIGH; - master->bits_per_word_mask = SPI_BPW_MASK(8); + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 8); master->num_chipselect = pdata->num_chipselect; master->setup = spi_clps711x_setup; - master->transfer_one_message = spi_clps711x_transfer_one_message; + master->prepare_message = spi_clps711x_prepare_message; + master->transfer_one = spi_clps711x_transfer_one; hw = spi_master_get_devdata(master); for (i = 0; i < master->num_chipselect; i++) { - hw->chipselect[i] = pdata->chipselect[i]; - if (!gpio_is_valid(hw->chipselect[i])) { - dev_err(&pdev->dev, "Invalid CS GPIO %i\n", i); - ret = -EINVAL; - goto err_out; - } - if (devm_gpio_request(&pdev->dev, hw->chipselect[i], NULL)) { + master->cs_gpios[i] = pdata->chipselect[i]; + ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i], + DRIVER_NAME); + if (ret) { dev_err(&pdev->dev, "Can't get CS GPIO %i\n", i); - ret = -EINVAL; goto err_out; } } @@ -211,29 +182,45 @@ static int spi_clps711x_probe(struct platform_device *pdev) ret = PTR_ERR(hw->spi_clk); goto err_out; } - hw->max_speed_hz = clk_get_rate(hw->spi_clk); + master->max_speed_hz = clk_get_rate(hw->spi_clk); - init_completion(&hw->done); platform_set_drvdata(pdev, master); + hw->syscon = syscon_regmap_lookup_by_pdevname("syscon.3"); + if (IS_ERR(hw->syscon)) { + ret = PTR_ERR(hw->syscon); + goto err_out; + } + + hw->syscon1 = syscon_regmap_lookup_by_pdevname("syscon.1"); + if (IS_ERR(hw->syscon1)) { + ret = PTR_ERR(hw->syscon1); + goto err_out; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hw->syncio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw->syncio)) { + ret = PTR_ERR(hw->syncio); + goto err_out; + } + /* Disable extended mode due hardware problems */ - clps_writew(clps_readw(SYSCON3) & ~SYSCON3_ADCCON, SYSCON3); + regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCON, 0); /* Clear possible pending interrupt */ - clps_readl(SYNCIO); + readl(hw->syncio); - ret = devm_request_irq(&pdev->dev, IRQ_SSEOTI, spi_clps711x_isr, 0, - dev_name(&pdev->dev), hw); - if (ret) { - dev_err(&pdev->dev, "Can't request IRQ\n"); + ret = devm_request_irq(&pdev->dev, irq, spi_clps711x_isr, 0, + dev_name(&pdev->dev), master); + if (ret) goto err_out; - } ret = devm_spi_register_master(&pdev->dev, master); if (!ret) { dev_info(&pdev->dev, "SPI bus driver initialized. Master clock %u Hz\n", - hw->max_speed_hz); + master->max_speed_hz); return 0; } diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index cabed8f9119e..e2fa628e55e7 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -77,8 +77,6 @@ struct mcfqspi { struct mcfqspi_cs_control *cs_control; wait_queue_head_t waitq; - - struct device *dev; }; static void mcfqspi_wr_qmr(struct mcfqspi *mcfqspi, u16 val) @@ -135,13 +133,13 @@ static void mcfqspi_cs_deselect(struct mcfqspi *mcfqspi, u8 chip_select, static int mcfqspi_cs_setup(struct mcfqspi *mcfqspi) { - return (mcfqspi->cs_control && mcfqspi->cs_control->setup) ? + return (mcfqspi->cs_control->setup) ? mcfqspi->cs_control->setup(mcfqspi->cs_control) : 0; } static void mcfqspi_cs_teardown(struct mcfqspi *mcfqspi) { - if (mcfqspi->cs_control && mcfqspi->cs_control->teardown) + if (mcfqspi->cs_control->teardown) mcfqspi->cs_control->teardown(mcfqspi->cs_control); } @@ -300,68 +298,45 @@ static void mcfqspi_transfer_msg16(struct mcfqspi *mcfqspi, unsigned count, } } -static int mcfqspi_transfer_one_message(struct spi_master *master, - struct spi_message *msg) +static void mcfqspi_set_cs(struct spi_device *spi, bool enable) { - struct mcfqspi *mcfqspi = spi_master_get_devdata(master); - struct spi_device *spi = msg->spi; - struct spi_transfer *t; - int status = 0; - - list_for_each_entry(t, &msg->transfers, transfer_list) { - bool cs_high = spi->mode & SPI_CS_HIGH; - u16 qmr = MCFQSPI_QMR_MSTR; - - qmr |= t->bits_per_word << 10; - if (spi->mode & SPI_CPHA) - qmr |= MCFQSPI_QMR_CPHA; - if (spi->mode & SPI_CPOL) - qmr |= MCFQSPI_QMR_CPOL; - if (t->speed_hz) - qmr |= mcfqspi_qmr_baud(t->speed_hz); - else - qmr |= mcfqspi_qmr_baud(spi->max_speed_hz); - mcfqspi_wr_qmr(mcfqspi, qmr); + struct mcfqspi *mcfqspi = spi_master_get_devdata(spi->master); + bool cs_high = spi->mode & SPI_CS_HIGH; + if (enable) mcfqspi_cs_select(mcfqspi, spi->chip_select, cs_high); + else + mcfqspi_cs_deselect(mcfqspi, spi->chip_select, cs_high); +} - mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE); - if (t->bits_per_word == 8) - mcfqspi_transfer_msg8(mcfqspi, t->len, t->tx_buf, - t->rx_buf); - else - mcfqspi_transfer_msg16(mcfqspi, t->len / 2, t->tx_buf, - t->rx_buf); - mcfqspi_wr_qir(mcfqspi, 0); - - if (t->delay_usecs) - udelay(t->delay_usecs); - if (t->cs_change) { - if (!list_is_last(&t->transfer_list, &msg->transfers)) - mcfqspi_cs_deselect(mcfqspi, spi->chip_select, - cs_high); - } else { - if (list_is_last(&t->transfer_list, &msg->transfers)) - mcfqspi_cs_deselect(mcfqspi, spi->chip_select, - cs_high); - } - msg->actual_length += t->len; - } - msg->status = status; - spi_finalize_current_message(master); - - return status; +static int mcfqspi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct mcfqspi *mcfqspi = spi_master_get_devdata(master); + u16 qmr = MCFQSPI_QMR_MSTR; + + qmr |= t->bits_per_word << 10; + if (spi->mode & SPI_CPHA) + qmr |= MCFQSPI_QMR_CPHA; + if (spi->mode & SPI_CPOL) + qmr |= MCFQSPI_QMR_CPOL; + qmr |= mcfqspi_qmr_baud(t->speed_hz); + mcfqspi_wr_qmr(mcfqspi, qmr); + + mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE); + if (t->bits_per_word == 8) + mcfqspi_transfer_msg8(mcfqspi, t->len, t->tx_buf, t->rx_buf); + else + mcfqspi_transfer_msg16(mcfqspi, t->len / 2, t->tx_buf, + t->rx_buf); + mcfqspi_wr_qir(mcfqspi, 0); + return 0; } static int mcfqspi_setup(struct spi_device *spi) { - if (spi->chip_select >= spi->master->num_chipselect) { - dev_dbg(&spi->dev, "%d chip select is out of range\n", - spi->chip_select); - return -EINVAL; - } - mcfqspi_cs_deselect(spi_master_get_devdata(spi->master), spi->chip_select, spi->mode & SPI_CS_HIGH); @@ -388,6 +363,11 @@ static int mcfqspi_probe(struct platform_device *pdev) return -ENOENT; } + if (!pdata->cs_control) { + dev_dbg(&pdev->dev, "pdata->cs_control is NULL\n"); + return -EINVAL; + } + master = spi_alloc_master(&pdev->dev, sizeof(*mcfqspi)); if (master == NULL) { dev_dbg(&pdev->dev, "spi_alloc_master failed\n"); @@ -436,12 +416,12 @@ static int mcfqspi_probe(struct platform_device *pdev) } init_waitqueue_head(&mcfqspi->waitq); - mcfqspi->dev = &pdev->dev; master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16); master->setup = mcfqspi_setup; - master->transfer_one_message = mcfqspi_transfer_one_message; + master->set_cs = mcfqspi_set_cs; + master->transfer_one = mcfqspi_transfer_one; master->auto_runtime_pm = true; platform_set_drvdata(pdev, master); @@ -451,7 +431,7 @@ static int mcfqspi_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "spi_register_master failed\n"); goto fail2; } - pm_runtime_enable(mcfqspi->dev); + pm_runtime_enable(&pdev->dev); dev_info(&pdev->dev, "Coldfire QSPI bus driver\n"); @@ -473,9 +453,8 @@ static int mcfqspi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct mcfqspi *mcfqspi = spi_master_get_devdata(master); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pm_runtime_disable(mcfqspi->dev); + pm_runtime_disable(&pdev->dev); /* disable the hardware (set the baud rate to 0) */ mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR); @@ -490,8 +469,11 @@ static int mcfqspi_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct mcfqspi *mcfqspi = spi_master_get_devdata(master); + int ret; - spi_master_suspend(master); + ret = spi_master_suspend(master); + if (ret) + return ret; clk_disable(mcfqspi->clk); @@ -503,18 +485,17 @@ static int mcfqspi_resume(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct mcfqspi *mcfqspi = spi_master_get_devdata(master); - spi_master_resume(master); - clk_enable(mcfqspi->clk); - return 0; + return spi_master_resume(master); } #endif #ifdef CONFIG_PM_RUNTIME static int mcfqspi_runtime_suspend(struct device *dev) { - struct mcfqspi *mcfqspi = dev_get_drvdata(dev); + struct spi_master *master = dev_get_drvdata(dev); + struct mcfqspi *mcfqspi = spi_master_get_devdata(master); clk_disable(mcfqspi->clk); @@ -523,7 +504,8 @@ static int mcfqspi_runtime_suspend(struct device *dev) static int mcfqspi_runtime_resume(struct device *dev) { - struct mcfqspi *mcfqspi = dev_get_drvdata(dev); + struct spi_master *master = dev_get_drvdata(dev); + struct mcfqspi *mcfqspi = spi_master_get_devdata(master); clk_enable(mcfqspi->clk); diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 5e7389faa2a0..50f750989258 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -802,8 +802,7 @@ static int spi_davinci_get_pdata(struct platform_device *pdev, pdata = &dspi->pdata; pdata->version = SPI_VERSION_1; - match = of_match_device(of_match_ptr(davinci_spi_of_match), - &pdev->dev); + match = of_match_device(davinci_spi_of_match, &pdev->dev); if (!match) return -ENODEV; @@ -824,7 +823,6 @@ static int spi_davinci_get_pdata(struct platform_device *pdev, return 0; } #else -#define davinci_spi_of_match NULL static struct davinci_spi_platform_data *spi_davinci_get_pdata(struct platform_device *pdev, struct davinci_spi *dspi) @@ -864,10 +862,6 @@ static int davinci_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); dspi = spi_master_get_devdata(master); - if (dspi == NULL) { - ret = -ENOENT; - goto free_master; - } if (dev_get_platdata(&pdev->dev)) { pdata = dev_get_platdata(&pdev->dev); @@ -908,10 +902,6 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; dspi->bitbang.master = master; - if (dspi->bitbang.master == NULL) { - ret = -ENODEV; - goto free_master; - } dspi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dspi->clk)) { @@ -1040,7 +1030,7 @@ static struct platform_driver davinci_spi_driver = { .driver = { .name = "spi_davinci", .owner = THIS_MODULE, - .of_match_table = davinci_spi_of_match, + .of_match_table = of_match_ptr(davinci_spi_of_match), }, .probe = davinci_spi_probe, .remove = davinci_spi_remove, diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 9af56cdf1540..1492f5ee9aaa 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -66,7 +66,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) if (ret) return ret; - dws->bus_num = 0; + dws->bus_num = pdev->id; dws->num_cs = 4; dws->max_freq = clk_get_rate(dwsmmio->clk); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index bf98d63d92b3..712ac5629cd4 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -276,8 +276,7 @@ static void giveback(struct dw_spi *dws) queue_work(dws->workqueue, &dws->pump_messages); spin_unlock_irqrestore(&dws->lock, flags); - last_transfer = list_entry(msg->transfers.prev, - struct spi_transfer, + last_transfer = list_last_entry(&msg->transfers, struct spi_transfer, transfer_list); if (!last_transfer->cs_change && dws->cs_control) @@ -439,12 +438,6 @@ static void pump_transfers(unsigned long data) if (transfer->speed_hz != speed) { speed = transfer->speed_hz; - if (speed > dws->max_freq) { - printk(KERN_ERR "MRST SPI0: unsupported" - "freq: %dHz\n", speed); - message->status = -EIO; - goto early_exit; - } /* clk_div doesn't support odd number */ clk_div = dws->max_freq / speed; @@ -671,12 +664,6 @@ static int dw_spi_setup(struct spi_device *spi) return 0; } -static void dw_spi_cleanup(struct spi_device *spi) -{ - struct chip_data *chip = spi_get_ctldata(spi); - kfree(chip); -} - static int init_queue(struct dw_spi *dws) { INIT_LIST_HEAD(&dws->queue); @@ -806,9 +793,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->bus_num = dws->bus_num; master->num_chipselect = dws->num_cs; - master->cleanup = dw_spi_cleanup; master->setup = dw_spi_setup; master->transfer = dw_spi_transfer; + master->max_speed_hz = dws->max_freq; /* Basic HW init */ spi_hw_init(dws); diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c index d4d3cc534792..be44a3eeb5e8 100644 --- a/drivers/spi/spi-efm32.c +++ b/drivers/spi/spi-efm32.c @@ -198,7 +198,7 @@ static int efm32_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) efm32_spi_filltx(ddata); - init_completion(&ddata->done); + reinit_completion(&ddata->done); efm32_spi_write32(ddata, REG_IF_TXBL | REG_IF_RXDATAV, REG_IEN); @@ -287,17 +287,17 @@ static u32 efm32_spi_get_configured_location(struct efm32_spi_ddata *ddata) return (reg & REG_ROUTE_LOCATION__MASK) >> __ffs(REG_ROUTE_LOCATION__MASK); } -static int efm32_spi_probe_dt(struct platform_device *pdev, +static void efm32_spi_probe_dt(struct platform_device *pdev, struct spi_master *master, struct efm32_spi_ddata *ddata) { struct device_node *np = pdev->dev.of_node; u32 location; int ret; - if (!np) - return 1; - - ret = of_property_read_u32(np, "location", &location); + ret = of_property_read_u32(np, "efm32,location", &location); + if (ret) + /* fall back to old and (wrongly) generic property "location" */ + ret = of_property_read_u32(np, "location", &location); if (!ret) { dev_dbg(&pdev->dev, "using location %u\n", location); } else { @@ -308,11 +308,6 @@ static int efm32_spi_probe_dt(struct platform_device *pdev, } ddata->pdata.location = location; - - /* spi core takes care about the bus number using an alias */ - master->bus_num = -1; - - return 0; } static int efm32_spi_probe(struct platform_device *pdev) @@ -322,9 +317,14 @@ static int efm32_spi_probe(struct platform_device *pdev) int ret; struct spi_master *master; struct device_node *np = pdev->dev.of_node; - unsigned int num_cs, i; + int num_cs, i; + + if (!np) + return -EINVAL; num_cs = of_gpio_named_count(np, "cs-gpios"); + if (num_cs < 0) + return num_cs; master = spi_alloc_master(&pdev->dev, sizeof(*ddata) + num_cs * sizeof(unsigned)); @@ -349,6 +349,7 @@ static int efm32_spi_probe(struct platform_device *pdev) ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs; spin_lock_init(&ddata->lock); + init_completion(&ddata->done); ddata->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(ddata->clk)) { @@ -415,23 +416,7 @@ static int efm32_spi_probe(struct platform_device *pdev) goto err; } - ret = efm32_spi_probe_dt(pdev, master, ddata); - if (ret > 0) { - /* not created by device tree */ - const struct efm32_spi_pdata *pdata = - dev_get_platdata(&pdev->dev); - - if (pdata) - ddata->pdata = *pdata; - else - ddata->pdata.location = - efm32_spi_get_configured_location(ddata); - - master->bus_num = pdev->id; - - } else if (ret < 0) { - goto err_disable_clk; - } + efm32_spi_probe_dt(pdev, master, ddata); efm32_spi_write32(ddata, 0, REG_IEN); efm32_spi_write32(ddata, REG_ROUTE_TXPEN | REG_ROUTE_RXPEN | @@ -487,6 +472,9 @@ static int efm32_spi_remove(struct platform_device *pdev) static const struct of_device_id efm32_spi_dt_ids[] = { { + .compatible = "energymicro,efm32-spi", + }, { + /* doesn't follow the "vendor,device" scheme, don't use */ .compatible = "efm32,spi", }, { /* sentinel */ diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index 1bfaed6e4073..2f675d32df0e 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -73,8 +73,6 @@ * @clk: clock for the controller * @regs_base: pointer to ioremap()'d registers * @sspdr_phys: physical address of the SSPDR register - * @min_rate: minimum clock rate (in Hz) supported by the controller - * @max_rate: maximum clock rate (in Hz) supported by the controller * @wait: wait here until given transfer is completed * @current_msg: message that is currently processed (or %NULL if none) * @tx: current byte in transfer to transmit @@ -95,8 +93,6 @@ struct ep93xx_spi { struct clk *clk; void __iomem *regs_base; unsigned long sspdr_phys; - unsigned long min_rate; - unsigned long max_rate; struct completion wait; struct spi_message *current_msg; size_t tx; @@ -199,9 +195,9 @@ static void ep93xx_spi_disable_interrupts(const struct ep93xx_spi *espi) * @div_scr: pointer to return the scr divider */ static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi, - unsigned long rate, - u8 *div_cpsr, u8 *div_scr) + u32 rate, u8 *div_cpsr, u8 *div_scr) { + struct spi_master *master = platform_get_drvdata(espi->pdev); unsigned long spi_clk_rate = clk_get_rate(espi->clk); int cpsr, scr; @@ -210,7 +206,7 @@ static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi, * controller. Note that minimum value is already checked in * ep93xx_spi_transfer_one_message(). */ - rate = clamp(rate, espi->min_rate, espi->max_rate); + rate = clamp(rate, master->min_speed_hz, master->max_speed_hz); /* * Calculate divisors so that we can get speed according the @@ -735,13 +731,6 @@ static int ep93xx_spi_transfer_one_message(struct spi_master *master, struct spi_message *msg) { struct ep93xx_spi *espi = spi_master_get_devdata(master); - struct spi_transfer *t; - - /* first validate each transfer */ - list_for_each_entry(t, &msg->transfers, transfer_list) { - if (t->speed_hz < espi->min_rate) - return -EINVAL; - } msg->state = NULL; msg->status = 0; @@ -917,8 +906,8 @@ static int ep93xx_spi_probe(struct platform_device *pdev) * Calculate maximum and minimum supported clock rates * for the controller. */ - espi->max_rate = clk_get_rate(espi->clk) / 2; - espi->min_rate = clk_get_rate(espi->clk) / (254 * 256); + master->max_speed_hz = clk_get_rate(espi->clk) / 2; + master->min_speed_hz = clk_get_rate(espi->clk) / (254 * 256); espi->pdev = pdev; espi->sspdr_phys = res->start + SSPDR; diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index dd5bd468e962..09965f069a1c 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -312,9 +312,6 @@ static int falcon_sflash_setup(struct spi_device *spi) unsigned int i; unsigned long flags; - if (spi->chip_select > 0) - return -ENODEV; - spin_lock_irqsave(&ebu_lock, flags); if (spi->max_speed_hz >= CLOCK_100M) { @@ -422,9 +419,7 @@ static int falcon_sflash_probe(struct platform_device *pdev) priv->master = master; master->mode_bits = SPI_MODE_3; - master->num_chipselect = 1; master->flags = SPI_MASTER_HALF_DUPLEX; - master->bus_num = -1; master->setup = falcon_sflash_setup; master->prepare_transfer_hardware = falcon_sflash_prepare_xfer; master->transfer_one_message = falcon_sflash_xfer_one; diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index ec79f726672a..d565eeee3bd8 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/errno.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/sched.h> #include <linux/delay.h> #include <linux/io.h> @@ -108,11 +109,11 @@ struct fsl_dspi { struct spi_bitbang bitbang; struct platform_device *pdev; - void __iomem *base; + struct regmap *regmap; int irq; - struct clk *clk; + struct clk *clk; - struct spi_transfer *cur_transfer; + struct spi_transfer *cur_transfer; struct chip_data *cur_chip; size_t len; void *tx; @@ -123,24 +124,17 @@ struct fsl_dspi { u8 cs; u16 void_write_data; - wait_queue_head_t waitq; - u32 waitflags; + wait_queue_head_t waitq; + u32 waitflags; }; static inline int is_double_byte_mode(struct fsl_dspi *dspi) { - return ((readl(dspi->base + SPI_CTAR(dspi->cs)) & SPI_FRAME_BITS_MASK) - == SPI_FRAME_BITS(8)) ? 0 : 1; -} + unsigned int val; -static void set_bit_mode(struct fsl_dspi *dspi, unsigned char bits) -{ - u32 temp; + regmap_read(dspi->regmap, SPI_CTAR(dspi->cs), &val); - temp = readl(dspi->base + SPI_CTAR(dspi->cs)); - temp &= ~SPI_FRAME_BITS_MASK; - temp |= SPI_FRAME_BITS(bits); - writel(temp, dspi->base + SPI_CTAR(dspi->cs)); + return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1; } static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, @@ -188,7 +182,8 @@ static int dspi_transfer_write(struct fsl_dspi *dspi) */ if (tx_word && (dspi->len == 1)) { dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - set_bit_mode(dspi, 8); + regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); tx_word = 0; } @@ -238,7 +233,8 @@ static int dspi_transfer_write(struct fsl_dspi *dspi) dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */ } - writel(dspi_pushr, dspi->base + SPI_PUSHR); + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); + tx_count++; } @@ -253,17 +249,23 @@ static int dspi_transfer_read(struct fsl_dspi *dspi) while ((dspi->rx < dspi->rx_end) && (rx_count < DSPI_FIFO_SIZE)) { if (rx_word) { + unsigned int val; + if ((dspi->rx_end - dspi->rx) == 1) break; - d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR)); + regmap_read(dspi->regmap, SPI_POPR, &val); + d = SPI_POPR_RXDATA(val); if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) *(u16 *)dspi->rx = d; dspi->rx += 2; } else { - d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR)); + unsigned int val; + + regmap_read(dspi->regmap, SPI_POPR, &val); + d = SPI_POPR_RXDATA(val); if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) *(u8 *)dspi->rx = d; dspi->rx++; @@ -295,13 +297,13 @@ static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t) if (!dspi->tx) dspi->dataflags |= TRAN_STATE_TX_VOID; - writel(dspi->cur_chip->mcr_val, dspi->base + SPI_MCR); - writel(dspi->cur_chip->ctar_val, dspi->base + SPI_CTAR(dspi->cs)); - writel(SPI_RSER_EOQFE, dspi->base + SPI_RSER); + 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) - writel(dspi->cur_chip->ctar_val, - dspi->base + SPI_CTAR(dspi->cs)); + regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), + dspi->cur_chip->ctar_val); dspi_transfer_write(dspi); @@ -315,7 +317,9 @@ static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t) static void dspi_chipselect(struct spi_device *spi, int value) { struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); - u32 pushr = readl(dspi->base + SPI_PUSHR); + unsigned int pushr; + + regmap_read(dspi->regmap, SPI_PUSHR, &pushr); switch (value) { case BITBANG_CS_ACTIVE: @@ -326,7 +330,7 @@ static void dspi_chipselect(struct spi_device *spi, int value) break; } - writel(pushr, dspi->base + SPI_PUSHR); + regmap_write(dspi->regmap, SPI_PUSHR, pushr); } static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) @@ -338,7 +342,8 @@ static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (chip == NULL) { - chip = kcalloc(1, sizeof(struct chip_data), GFP_KERNEL); + chip = devm_kzalloc(&spi->dev, sizeof(struct chip_data), + GFP_KERNEL); if (!chip) return -ENOMEM; } @@ -349,7 +354,6 @@ static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) fmsz = spi->bits_per_word - 1; } else { pr_err("Invalid wordsize\n"); - kfree(chip); return -ENODEV; } @@ -382,13 +386,15 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) { struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; - writel(SPI_SR_EOQF, dspi->base + SPI_SR); + regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF); dspi_transfer_read(dspi); if (!dspi->len) { if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) - set_bit_mode(dspi, 16); + regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16)); + dspi->waitflags = 1; wake_up_interruptible(&dspi->waitq); } else { @@ -420,7 +426,6 @@ static int dspi_suspend(struct device *dev) static int dspi_resume(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); struct fsl_dspi *dspi = spi_master_get_devdata(master); @@ -431,8 +436,13 @@ static int dspi_resume(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -static const struct dev_pm_ops dspi_pm = { - SET_SYSTEM_SLEEP_PM_OPS(dspi_suspend, dspi_resume) +static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); + +static struct regmap_config dspi_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x88, }; static int dspi_probe(struct platform_device *pdev) @@ -441,6 +451,7 @@ static int dspi_probe(struct platform_device *pdev) struct spi_master *master; struct fsl_dspi *dspi; struct resource *res; + void __iomem *base; int ret = 0, cs_num, bus_num; master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi)); @@ -475,12 +486,24 @@ static int dspi_probe(struct platform_device *pdev) master->bus_num = bus_num; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dspi->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dspi->base)) { - ret = PTR_ERR(dspi->base); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); goto out_master_put; } + dspi_regmap_config.lock_arg = dspi; + dspi_regmap_config.val_format_endian = + of_property_read_bool(np, "big-endian") + ? REGMAP_ENDIAN_BIG : REGMAP_ENDIAN_DEFAULT; + dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base, + &dspi_regmap_config); + if (IS_ERR(dspi->regmap)) { + dev_err(&pdev->dev, "failed to init regmap: %ld\n", + PTR_ERR(dspi->regmap)); + return PTR_ERR(dspi->regmap); + } + dspi->irq = platform_get_irq(pdev, 0); if (dspi->irq < 0) { dev_err(&pdev->dev, "can't get platform irq\n"); @@ -504,7 +527,7 @@ static int dspi_probe(struct platform_device *pdev) clk_prepare_enable(dspi->clk); init_waitqueue_head(&dspi->waitq); - platform_set_drvdata(pdev, dspi); + platform_set_drvdata(pdev, master); ret = spi_bitbang_start(&dspi->bitbang); if (ret != 0) { @@ -525,7 +548,8 @@ out_master_put: static int dspi_remove(struct platform_device *pdev) { - struct fsl_dspi *dspi = platform_get_drvdata(pdev); + struct spi_master *master = platform_get_drvdata(pdev); + struct fsl_dspi *dspi = spi_master_get_devdata(master); /* Disconnect from the SPI framework */ spi_bitbang_stop(&dspi->bitbang); diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 428dc7a6b62e..6fb2b75df821 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -219,13 +219,8 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base; unsigned int len = t->len; - u8 bits_per_word; int ret; - bits_per_word = spi->bits_per_word; - if (t->bits_per_word) - bits_per_word = t->bits_per_word; - mpc8xxx_spi->len = t->len; len = roundup(len, 4) / 4; diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index 0b75f26158ab..e5d45fca3551 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -200,7 +200,7 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev) const void *prop; int ret = -ENOMEM; - pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); + pinfo = devm_kzalloc(&ofdev->dev, sizeof(*pinfo), GFP_KERNEL); if (!pinfo) return -ENOMEM; @@ -215,15 +215,13 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev) pdata->sysclk = get_brgfreq(); if (pdata->sysclk == -1) { pdata->sysclk = fsl_get_sys_freq(); - if (pdata->sysclk == -1) { - ret = -ENODEV; - goto err; - } + if (pdata->sysclk == -1) + return -ENODEV; } #else ret = of_property_read_u32(np, "clock-frequency", &pdata->sysclk); if (ret) - goto err; + return ret; #endif prop = of_get_property(np, "mode", NULL); @@ -237,8 +235,4 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev) pdata->flags = SPI_CPM_MODE | SPI_CPM1; return 0; - -err: - kfree(pinfo); - return ret; } diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 119f7af94537..f35488ed62a9 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -239,12 +239,6 @@ static int fsl_spi_setup_transfer(struct spi_device *spi, if (!bits_per_word) bits_per_word = spi->bits_per_word; - /* Make sure its a bit width we support [4..16, 32] */ - if ((bits_per_word < 4) - || ((bits_per_word > 16) && (bits_per_word != 32)) - || (bits_per_word > mpc8xxx_spi->max_bits_per_word)) - return -EINVAL; - if (!hz) hz = spi->max_speed_hz; @@ -362,18 +356,28 @@ static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t, static void fsl_spi_do_one_msg(struct spi_message *m) { struct spi_device *spi = m->spi; - struct spi_transfer *t; + struct spi_transfer *t, *first; unsigned int cs_change; const int nsecs = 50; int status; - cs_change = 1; - status = 0; + /* Don't allow changes if CS is active */ + first = list_first_entry(&m->transfers, struct spi_transfer, + transfer_list); list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->bits_per_word || t->speed_hz) { - /* Don't allow changes if CS is active */ + if ((first->bits_per_word != t->bits_per_word) || + (first->speed_hz != t->speed_hz)) { status = -EINVAL; + dev_err(&spi->dev, + "bits_per_word/speed_hz should be same for the same SPI transfer\n"); + return; + } + } + cs_change = 1; + status = -EINVAL; + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->bits_per_word || t->speed_hz) { if (cs_change) status = fsl_spi_setup_transfer(spi, t); if (status < 0) @@ -641,6 +645,10 @@ static struct spi_master * fsl_spi_probe(struct device *dev, if (mpc8xxx_spi->type == TYPE_GRLIB) fsl_spi_grlib_probe(dev); + master->bits_per_word_mask = + (SPI_BPW_RANGE_MASK(4, 16) | SPI_BPW_MASK(32)) & + SPI_BPW_RANGE_MASK(1, mpc8xxx_spi->max_bits_per_word); + if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) mpc8xxx_spi->set_shifts = fsl_spi_qe_cpu_set_shifts; diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 7beeb29472ac..09823076df88 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -19,7 +19,6 @@ */ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/of.h> @@ -250,7 +249,7 @@ static int spi_gpio_setup(struct spi_device *spi) /* * ... otherwise, take it from spi->controller_data */ - cs = (unsigned int) spi->controller_data; + cs = (unsigned int)(uintptr_t) spi->controller_data; } if (!spi->controller_state) { @@ -503,13 +502,12 @@ static int spi_gpio_remove(struct platform_device *pdev) { struct spi_gpio *spi_gpio; struct spi_gpio_platform_data *pdata; - int status; spi_gpio = platform_get_drvdata(pdev); pdata = dev_get_platdata(&pdev->dev); /* stop() unregisters child devices too */ - status = spi_bitbang_stop(&spi_gpio->bitbang); + spi_bitbang_stop(&spi_gpio->bitbang); if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); @@ -518,7 +516,7 @@ static int spi_gpio_remove(struct platform_device *pdev) gpio_free(SPI_SCK_GPIO); spi_master_put(spi_gpio->bitbang.master); - return status; + return 0; } MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index a5474ef9d2a0..5daff2054ae4 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -23,7 +23,6 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> @@ -741,7 +740,7 @@ static int spi_imx_transfer(struct spi_device *spi, spi_imx->count = transfer->len; spi_imx->txfifo = 0; - init_completion(&spi_imx->xfer_done); + reinit_completion(&spi_imx->xfer_done); spi_imx_push(spi_imx); @@ -880,12 +879,12 @@ static int spi_imx_probe(struct platform_device *pdev) spi_imx->irq = platform_get_irq(pdev, 0); if (spi_imx->irq < 0) { - ret = -EINVAL; + ret = spi_imx->irq; goto out_master_put; } ret = devm_request_irq(&pdev->dev, spi_imx->irq, spi_imx_isr, 0, - DRIVER_NAME, spi_imx); + dev_name(&pdev->dev), spi_imx); if (ret) { dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret); goto out_master_put; @@ -948,8 +947,8 @@ static int spi_imx_remove(struct platform_device *pdev) spi_bitbang_stop(&spi_imx->bitbang); writel(0, spi_imx->base + MXC_CSPICTRL); - clk_disable_unprepare(spi_imx->clk_ipg); - clk_disable_unprepare(spi_imx->clk_per); + clk_unprepare(spi_imx->clk_ipg); + clk_unprepare(spi_imx->clk_per); spi_master_put(master); return 0; diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 5032141eeeec..3822eef2ef9d 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -16,7 +16,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/of_address.h> @@ -466,10 +465,8 @@ static void mpc512x_spi_cs_control(struct spi_device *spi, bool onoff) gpio_set_value(spi->cs_gpio, onoff); } -/* bus_num is used only for the case dev->platform_data == NULL */ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, - u32 size, unsigned int irq, - s16 bus_num) + u32 size, unsigned int irq) { struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct mpc512x_psc_spi *mps; @@ -488,7 +485,6 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, if (pdata == NULL) { mps->cs_control = mpc512x_spi_cs_control; - master->bus_num = bus_num; } else { mps->cs_control = pdata->cs_control; master->bus_num = pdata->bus_num; @@ -574,7 +570,6 @@ static int mpc512x_psc_spi_of_probe(struct platform_device *op) { const u32 *regaddr_p; u64 regaddr64, size64; - s16 id = -1; regaddr_p = of_get_address(op->dev.of_node, 0, &size64, NULL); if (!regaddr_p) { @@ -583,16 +578,8 @@ static int mpc512x_psc_spi_of_probe(struct platform_device *op) } regaddr64 = of_translate_address(op->dev.of_node, regaddr_p); - /* get PSC id (0..11, used by port_config) */ - id = of_alias_get_id(op->dev.of_node, "spi"); - if (id < 0) { - dev_err(&op->dev, "no alias id for %s\n", - op->dev.of_node->full_name); - return id; - } - return mpc512x_psc_spi_do_probe(&op->dev, (u32) regaddr64, (u32) size64, - irq_of_parse_and_map(op->dev.of_node, 0), id); + irq_of_parse_and_map(op->dev.of_node, 0)); } static int mpc512x_psc_spi_of_remove(struct platform_device *op) diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c index 00ba910ab302..3d18d9351185 100644 --- a/drivers/spi/spi-mpc52xx-psc.c +++ b/drivers/spi/spi-mpc52xx-psc.c @@ -12,7 +12,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/interrupt.h> diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index 7c675fe83101..aac2a5ddd964 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -12,7 +12,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/errno.h> #include <linux/of_platform.h> #include <linux/interrupt.h> @@ -357,20 +356,6 @@ static void mpc52xx_spi_wq(struct work_struct *work) * spi_master ops */ -static int mpc52xx_spi_setup(struct spi_device *spi) -{ - if (spi->bits_per_word % 8) - return -EINVAL; - - if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) - return -EINVAL; - - if (spi->chip_select >= spi->master->num_chipselect) - return -EINVAL; - - return 0; -} - static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m) { struct mpc52xx_spi *ms = spi_master_get_devdata(spi->master); @@ -433,9 +418,9 @@ static int mpc52xx_spi_probe(struct platform_device *op) goto err_alloc; } - master->setup = mpc52xx_spi_setup; master->transfer = mpc52xx_spi_transfer; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + master->bits_per_word_mask = SPI_BPW_MASK(8); master->dev.of_node = op->dev.of_node; platform_set_drvdata(op, master); diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 79e5aa2250c8..2884f0c2f5f0 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -29,7 +29,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/ioport.h> #include <linux/of.h> #include <linux/of_device.h> @@ -371,7 +370,7 @@ static int mxs_spi_transfer_one(struct spi_master *master, { struct mxs_spi *spi = spi_master_get_devdata(master); struct mxs_ssp *ssp = &spi->ssp; - struct spi_transfer *t, *tmp_t; + struct spi_transfer *t; unsigned int flag; int status = 0; @@ -381,7 +380,7 @@ static int mxs_spi_transfer_one(struct spi_master *master, writel(mxs_spi_cs_to_reg(m->spi->chip_select), ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); - list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { + list_for_each_entry(t, &m->transfers, transfer_list) { status = mxs_spi_setup_transfer(m->spi, t); if (status) @@ -473,7 +472,7 @@ static int mxs_spi_probe(struct platform_device *pdev) iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq_err = platform_get_irq(pdev, 0); if (irq_err < 0) - return -EINVAL; + return irq_err; base = devm_ioremap_resource(&pdev->dev, iores); if (IS_ERR(base)) diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index bae97ffec4b9..16e30de650b0 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -9,7 +9,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/interrupt.h> @@ -38,7 +37,9 @@ /* usi register bit */ #define ENINT (0x01 << 17) #define ENFLG (0x01 << 16) +#define SLEEP (0x0f << 12) #define TXNUM (0x03 << 8) +#define TXBITLEN (0x1f << 3) #define TXNEG (0x01 << 2) #define RXNEG (0x01 << 1) #define LSB (0x01 << 10) @@ -58,11 +59,8 @@ struct nuc900_spi { unsigned char *rx; struct clk *clk; struct spi_master *master; - struct spi_device *curdev; - struct device *dev; struct nuc900_spi_info *pdata; spinlock_t lock; - struct resource *res; }; static inline struct nuc900_spi *to_hw(struct spi_device *sdev) @@ -119,19 +117,16 @@ static void nuc900_spi_chipsel(struct spi_device *spi, int value) } } -static void nuc900_spi_setup_txnum(struct nuc900_spi *hw, - unsigned int txnum) +static void nuc900_spi_setup_txnum(struct nuc900_spi *hw, unsigned int txnum) { unsigned int val; unsigned long flags; spin_lock_irqsave(&hw->lock, flags); - val = __raw_readl(hw->regs + USI_CNT); + val = __raw_readl(hw->regs + USI_CNT) & ~TXNUM; - if (!txnum) - val &= ~TXNUM; - else + if (txnum) val |= txnum << 0x08; __raw_writel(val, hw->regs + USI_CNT); @@ -148,7 +143,7 @@ static void nuc900_spi_setup_txbitlen(struct nuc900_spi *hw, spin_lock_irqsave(&hw->lock, flags); - val = __raw_readl(hw->regs + USI_CNT); + val = __raw_readl(hw->regs + USI_CNT) & ~TXBITLEN; val |= (txbitlen << 0x03); @@ -287,12 +282,11 @@ static void nuc900_set_sleep(struct nuc900_spi *hw, unsigned int sleep) spin_lock_irqsave(&hw->lock, flags); - val = __raw_readl(hw->regs + USI_CNT); + val = __raw_readl(hw->regs + USI_CNT) & ~SLEEP; if (sleep) val |= (sleep << 12); - else - val &= ~(0x0f << 12); + __raw_writel(val, hw->regs + USI_CNT); spin_unlock_irqrestore(&hw->lock, flags); @@ -338,6 +332,7 @@ static int nuc900_spi_probe(struct platform_device *pdev) { struct nuc900_spi *hw; struct spi_master *master; + struct resource *res; int err = 0; master = spi_alloc_master(&pdev->dev, sizeof(struct nuc900_spi)); @@ -349,7 +344,6 @@ static int nuc900_spi_probe(struct platform_device *pdev) hw = spi_master_get_devdata(master); hw->master = master; hw->pdata = dev_get_platdata(&pdev->dev); - hw->dev = &pdev->dev; if (hw->pdata == NULL) { dev_err(&pdev->dev, "No platform data supplied\n"); @@ -369,8 +363,8 @@ static int nuc900_spi_probe(struct platform_device *pdev) hw->bitbang.chipselect = nuc900_spi_chipsel; hw->bitbang.txrx_bufs = nuc900_spi_txrx; - hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hw->regs = devm_ioremap_resource(&pdev->dev, hw->res); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hw->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(hw->regs)) { err = PTR_ERR(hw->regs); goto err_pdata; diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index f7c896e2981e..8998d11c7238 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -15,7 +15,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/errno.h> #include <linux/module.h> @@ -267,8 +266,6 @@ static int tiny_spi_probe(struct platform_device *pdev) /* setup the state for the bitbang driver */ hw->bitbang.master = master; - if (!hw->bitbang.master) - return err; hw->bitbang.setup_transfer = tiny_spi_setup_transfer; hw->bitbang.chipselect = tiny_spi_chipselect; hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs; diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index 67249a48b391..c5e2f718eebd 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -11,7 +11,6 @@ #include <linux/spi/spi.h> #include <linux/module.h> #include <linux/delay.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/of.h> @@ -33,13 +32,6 @@ struct octeon_spi { u64 cs_enax; }; -struct octeon_spi_setup { - u32 max_speed_hz; - u8 chip_select; - u8 mode; - u8 bits_per_word; -}; - static void octeon_spi_wait_ready(struct octeon_spi *p) { union cvmx_mpi_sts mpi_sts; @@ -57,6 +49,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, struct spi_transfer *xfer, bool last_xfer) { + struct spi_device *spi = msg->spi; union cvmx_mpi_cfg mpi_cfg; union cvmx_mpi_tx mpi_tx; unsigned int clkdiv; @@ -68,18 +61,11 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, int len; int i; - struct octeon_spi_setup *msg_setup = spi_get_ctldata(msg->spi); - - speed_hz = msg_setup->max_speed_hz; - mode = msg_setup->mode; + mode = spi->mode; cpha = mode & SPI_CPHA; cpol = mode & SPI_CPOL; - if (xfer->speed_hz) - speed_hz = xfer->speed_hz; - - if (speed_hz > OCTEON_SPI_MAX_CLOCK_HZ) - speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; + speed_hz = xfer->speed_hz ? : spi->max_speed_hz; clkdiv = octeon_get_io_clock_rate() / (2 * speed_hz); @@ -93,8 +79,8 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, mpi_cfg.s.cslate = cpha ? 1 : 0; mpi_cfg.s.enable = 1; - if (msg_setup->chip_select < 4) - p->cs_enax |= 1ull << (12 + msg_setup->chip_select); + if (spi->chip_select < 4) + p->cs_enax |= 1ull << (12 + spi->chip_select); mpi_cfg.u64 |= p->cs_enax; if (mpi_cfg.u64 != p->last_cfg) { @@ -114,7 +100,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); } mpi_tx.u64 = 0; - mpi_tx.s.csid = msg_setup->chip_select; + mpi_tx.s.csid = spi->chip_select; mpi_tx.s.leavecs = 1; mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; @@ -139,7 +125,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, } mpi_tx.u64 = 0; - mpi_tx.s.csid = msg_setup->chip_select; + mpi_tx.s.csid = spi->chip_select; if (last_xfer) mpi_tx.s.leavecs = xfer->cs_change; else @@ -169,17 +155,9 @@ static int octeon_spi_transfer_one_message(struct spi_master *master, int status = 0; struct spi_transfer *xfer; - /* - * We better have set the configuration via a call to .setup - * before we get here. - */ - if (spi_get_ctldata(msg->spi) == NULL) { - status = -EINVAL; - goto err; - } - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - bool last_xfer = &xfer->transfer_list == msg->transfers.prev; + bool last_xfer = list_is_last(&xfer->transfer_list, + &msg->transfers); int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer); if (r < 0) { status = r; @@ -194,41 +172,6 @@ err: return status; } -static struct octeon_spi_setup *octeon_spi_new_setup(struct spi_device *spi) -{ - struct octeon_spi_setup *setup = kzalloc(sizeof(*setup), GFP_KERNEL); - if (!setup) - return NULL; - - setup->max_speed_hz = spi->max_speed_hz; - setup->chip_select = spi->chip_select; - setup->mode = spi->mode; - setup->bits_per_word = spi->bits_per_word; - return setup; -} - -static int octeon_spi_setup(struct spi_device *spi) -{ - struct octeon_spi_setup *new_setup; - struct octeon_spi_setup *old_setup = spi_get_ctldata(spi); - - new_setup = octeon_spi_new_setup(spi); - if (!new_setup) - return -ENOMEM; - - spi_set_ctldata(spi, new_setup); - kfree(old_setup); - - return 0; -} - -static void octeon_spi_cleanup(struct spi_device *spi) -{ - struct octeon_spi_setup *old_setup = spi_get_ctldata(spi); - spi_set_ctldata(spi, NULL); - kfree(old_setup); -} - static int octeon_spi_probe(struct platform_device *pdev) { struct resource *res_mem; @@ -257,8 +200,6 @@ static int octeon_spi_probe(struct platform_device *pdev) p->register_base = (u64)devm_ioremap(&pdev->dev, res_mem->start, resource_size(res_mem)); - /* Dynamic bus numbering */ - master->bus_num = -1; master->num_chipselect = 4; master->mode_bits = SPI_CPHA | SPI_CPOL | @@ -266,10 +207,9 @@ static int octeon_spi_probe(struct platform_device *pdev) SPI_LSB_FIRST | SPI_3WIRE; - master->setup = octeon_spi_setup; - master->cleanup = octeon_spi_cleanup; master->transfer_one_message = octeon_spi_transfer_one_message; master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; master->dev.of_node = pdev->dev.of_node; err = devm_spi_register_master(&pdev->dev, master); diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 0d32054bfc0d..e7ffcded4e14 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -83,15 +83,11 @@ #define SPI_SHUTDOWN 1 struct omap1_spi100k { - struct spi_master *master; struct clk *ick; struct clk *fck; /* Virtual base address of the controller */ void __iomem *base; - - /* State of the SPI */ - unsigned int state; }; struct omap1_spi100k_cs { @@ -99,13 +95,6 @@ struct omap1_spi100k_cs { int word_len; }; -#define MOD_REG_BIT(val, mask, set) do { \ - if (set) \ - val |= mask; \ - else \ - val &= ~mask; \ -} while (0) - static void spi100k_enable_clock(struct spi_master *master) { unsigned int val; @@ -139,7 +128,7 @@ static void spi100k_write_data(struct spi_master *master, int len, int data) } spi100k_enable_clock(master); - writew( data , spi100k->base + SPI_TX_MSB); + writew(data , spi100k->base + SPI_TX_MSB); writew(SPI_CTRL_SEN(0) | SPI_CTRL_WORD_SIZE(len) | @@ -147,7 +136,8 @@ static void spi100k_write_data(struct spi_master *master, int len, int data) spi100k->base + SPI_CTRL); /* Wait for bit ack send change */ - while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE); + while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE) + ; udelay(1000); spi100k_disable_clock(master); @@ -155,7 +145,7 @@ static void spi100k_write_data(struct spi_master *master, int len, int data) static int spi100k_read_data(struct spi_master *master, int len) { - int dataH,dataL; + int dataH, dataL; struct omap1_spi100k *spi100k = spi_master_get_devdata(master); /* Always do at least 16 bits */ @@ -168,7 +158,8 @@ static int spi100k_read_data(struct spi_master *master, int len) SPI_CTRL_RD, spi100k->base + SPI_CTRL); - while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD); + while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD) + ; udelay(1000); dataL = readw(spi100k->base + SPI_RX_LSB); @@ -204,12 +195,10 @@ static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable) static unsigned omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) { - struct omap1_spi100k *spi100k; struct omap1_spi100k_cs *cs = spi->controller_state; unsigned int count, c; int word_len; - spi100k = spi_master_get_devdata(spi->master); count = xfer->len; c = count; word_len = cs->word_len; @@ -221,12 +210,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx = xfer->rx_buf; tx = xfer->tx_buf; do { - c-=1; + c -= 1; if (xfer->tx_buf != NULL) spi100k_write_data(spi->master, word_len, *tx++); if (xfer->rx_buf != NULL) *rx++ = spi100k_read_data(spi->master, word_len); - } while(c); + } while (c); } else if (word_len <= 16) { u16 *rx; const u16 *tx; @@ -234,12 +223,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx = xfer->rx_buf; tx = xfer->tx_buf; do { - c-=2; + c -= 2; if (xfer->tx_buf != NULL) - spi100k_write_data(spi->master,word_len, *tx++); + spi100k_write_data(spi->master, word_len, *tx++); if (xfer->rx_buf != NULL) - *rx++ = spi100k_read_data(spi->master,word_len); - } while(c); + *rx++ = spi100k_read_data(spi->master, word_len); + } while (c); } else if (word_len <= 32) { u32 *rx; const u32 *tx; @@ -247,12 +236,12 @@ omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) rx = xfer->rx_buf; tx = xfer->tx_buf; do { - c-=4; + c -= 4; if (xfer->tx_buf != NULL) - spi100k_write_data(spi->master,word_len, *tx); + spi100k_write_data(spi->master, word_len, *tx); if (xfer->rx_buf != NULL) - *rx = spi100k_read_data(spi->master,word_len); - } while(c); + *rx = spi100k_read_data(spi->master, word_len); + } while (c); } return count - c; } @@ -294,7 +283,7 @@ static int omap1_spi100k_setup(struct spi_device *spi) spi100k = spi_master_get_devdata(spi->master); if (!cs) { - cs = kzalloc(sizeof *cs, GFP_KERNEL); + cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL); if (!cs) return -ENOMEM; cs->base = spi100k->base + spi->chip_select * 0x14; @@ -411,14 +400,14 @@ static int omap1_spi100k_probe(struct platform_device *pdev) if (!pdev->id) return -EINVAL; - master = spi_alloc_master(&pdev->dev, sizeof *spi100k); + master = spi_alloc_master(&pdev->dev, sizeof(*spi100k)); if (master == NULL) { dev_dbg(&pdev->dev, "master allocation failed\n"); return -ENOMEM; } if (pdev->id != -1) - master->bus_num = pdev->id; + master->bus_num = pdev->id; master->setup = omap1_spi100k_setup; master->transfer_one_message = omap1_spi100k_transfer_one_message; @@ -434,7 +423,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); spi100k = spi_master_get_devdata(master); - spi100k->master = master; /* * The memory region base address is taken as the platform_data. @@ -461,8 +449,6 @@ static int omap1_spi100k_probe(struct platform_device *pdev) if (status < 0) goto err; - spi100k->state = SPI_RUNNING; - return status; err: diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index 9313fd3b413d..be2a2e108e2f 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -99,7 +99,6 @@ struct uwire_spi { }; struct uwire_state { - unsigned bits_per_word; unsigned div1_idx; }; @@ -210,9 +209,8 @@ static void uwire_chipselect(struct spi_device *spi, int value) static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t) { - struct uwire_state *ust = spi->controller_state; unsigned len = t->len; - unsigned bits = ust->bits_per_word; + unsigned bits = t->bits_per_word ? : spi->bits_per_word; unsigned bytes; u16 val, w; int status = 0; @@ -220,10 +218,6 @@ static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t) if (!t->tx_buf && !t->rx_buf) return 0; - /* Microwire doesn't read and write concurrently */ - if (t->tx_buf && t->rx_buf) - return -EPERM; - w = spi->chip_select << 10; w |= CS_CMD; @@ -322,7 +316,6 @@ static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t) struct uwire_state *ust = spi->controller_state; struct uwire_spi *uwire; unsigned flags = 0; - unsigned bits; unsigned hz; unsigned long rate; int div1_idx; @@ -332,23 +325,6 @@ static int uwire_setup_transfer(struct spi_device *spi, struct spi_transfer *t) uwire = spi_master_get_devdata(spi->master); - if (spi->chip_select > 3) { - pr_debug("%s: cs%d?\n", dev_name(&spi->dev), spi->chip_select); - status = -ENODEV; - goto done; - } - - bits = spi->bits_per_word; - if (t != NULL && t->bits_per_word) - bits = t->bits_per_word; - - if (bits > 16) { - pr_debug("%s: wordsize %d?\n", dev_name(&spi->dev), bits); - status = -ENODEV; - goto done; - } - ust->bits_per_word = bits; - /* mode 0..3, clock inverted separately; * standard nCS signaling; * don't treat DI=high as "not ready" @@ -502,6 +478,7 @@ static int uwire_probe(struct platform_device *pdev) status = PTR_ERR(uwire->ck); dev_dbg(&pdev->dev, "no functional clock?\n"); spi_master_put(master); + iounmap(uwire_base); return status; } clk_enable(uwire->ck); @@ -515,7 +492,7 @@ static int uwire_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); master->flags = SPI_MASTER_HALF_DUPLEX; master->bus_num = 2; /* "official" */ @@ -539,14 +516,13 @@ static int uwire_probe(struct platform_device *pdev) static int uwire_remove(struct platform_device *pdev) { struct uwire_spi *uwire = platform_get_drvdata(pdev); - int status; // FIXME remove all child devices, somewhere ... - status = spi_bitbang_stop(&uwire->bitbang); + spi_bitbang_stop(&uwire->bitbang); uwire_off(uwire); iounmap(uwire_base); - return status; + return 0; } /* work with hotplug and coldplug */ diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index a72127f08e39..2941c5b96ebc 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -22,7 +22,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/device.h> @@ -45,6 +44,7 @@ #include <linux/platform_data/spi-omap2-mcspi.h> #define OMAP2_MCSPI_MAX_FREQ 48000000 +#define OMAP2_MCSPI_MAX_DIVIDER 4096 #define OMAP2_MCSPI_MAX_FIFODEPTH 64 #define OMAP2_MCSPI_MAX_FIFOWCNT 0xFFFF #define SPI_AUTOSUSPEND_TIMEOUT 2000 @@ -89,6 +89,7 @@ #define OMAP2_MCSPI_CHCONF_FORCE BIT(20) #define OMAP2_MCSPI_CHCONF_FFET BIT(27) #define OMAP2_MCSPI_CHCONF_FFER BIT(28) +#define OMAP2_MCSPI_CHCONF_CLKG BIT(29) #define OMAP2_MCSPI_CHSTAT_RXS BIT(0) #define OMAP2_MCSPI_CHSTAT_TXS BIT(1) @@ -96,6 +97,7 @@ #define OMAP2_MCSPI_CHSTAT_TXFFE BIT(3) #define OMAP2_MCSPI_CHCTRL_EN BIT(0) +#define OMAP2_MCSPI_CHCTRL_EXTCLK_MASK (0xff << 8) #define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0) @@ -149,7 +151,7 @@ struct omap2_mcspi_cs { int word_len; struct list_head node; /* Context save and restore shadow register */ - u32 chconf0; + u32 chconf0, chctrl0; }; static inline void mcspi_write_reg(struct spi_master *master, @@ -230,10 +232,16 @@ static void omap2_mcspi_set_dma_req(const struct spi_device *spi, static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) { + struct omap2_mcspi_cs *cs = spi->controller_state; u32 l; - l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0; - mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l); + l = cs->chctrl0; + if (enable) + l |= OMAP2_MCSPI_CHCTRL_EN; + else + l &= ~OMAP2_MCSPI_CHCTRL_EN; + cs->chctrl0 = l; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, cs->chctrl0); /* Flash post-writes */ mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0); } @@ -840,7 +848,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi *mcspi; struct spi_master *spi_cntrl; - u32 l = 0, div = 0; + u32 l = 0, clkd = 0, div, extclk = 0, clkg = 0; u8 word_len = spi->bits_per_word; u32 speed_hz = spi->max_speed_hz; @@ -856,7 +864,17 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, speed_hz = t->speed_hz; speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ); - div = omap2_mcspi_calc_divisor(speed_hz); + if (speed_hz < (OMAP2_MCSPI_MAX_FREQ / OMAP2_MCSPI_MAX_DIVIDER)) { + clkd = omap2_mcspi_calc_divisor(speed_hz); + speed_hz = OMAP2_MCSPI_MAX_FREQ >> clkd; + clkg = 0; + } else { + div = (OMAP2_MCSPI_MAX_FREQ + speed_hz - 1) / speed_hz; + speed_hz = OMAP2_MCSPI_MAX_FREQ / div; + clkd = (div - 1) & 0xf; + extclk = (div - 1) >> 4; + clkg = OMAP2_MCSPI_CHCONF_CLKG; + } l = mcspi_cached_chconf0(spi); @@ -885,7 +903,16 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, /* set clock divisor */ l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK; - l |= div << 2; + l |= clkd << 2; + + /* set clock granularity */ + l &= ~OMAP2_MCSPI_CHCONF_CLKG; + l |= clkg; + if (clkg) { + cs->chctrl0 &= ~OMAP2_MCSPI_CHCTRL_EXTCLK_MASK; + cs->chctrl0 |= extclk << 8; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, cs->chctrl0); + } /* set SPI mode 0..3 */ if (spi->mode & SPI_CPOL) @@ -900,7 +927,7 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, mcspi_write_chconf0(spi, l); dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", - OMAP2_MCSPI_MAX_FREQ >> div, + speed_hz, (spi->mode & SPI_CPHA) ? "trailing" : "leading", (spi->mode & SPI_CPOL) ? "inverted" : "normal"); @@ -972,6 +999,7 @@ static int omap2_mcspi_setup(struct spi_device *spi) cs->base = mcspi->base + spi->chip_select * 0x14; cs->phys = mcspi->phys + spi->chip_select * 0x14; cs->chconf0 = 0; + cs->chctrl0 = 0; spi->controller_state = cs; /* Link this to context save list */ list_add_tail(&cs->node, &ctx->cs); @@ -1057,12 +1085,15 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) status = -EINVAL; break; } - if (par_override || t->speed_hz || t->bits_per_word) { + if (par_override || + (t->speed_hz != spi->max_speed_hz) || + (t->bits_per_word != spi->bits_per_word)) { par_override = 1; status = omap2_mcspi_setup_transfer(spi, t); if (status < 0) break; - if (!t->speed_hz && !t->bits_per_word) + if (t->speed_hz == spi->max_speed_hz && + t->bits_per_word == spi->bits_per_word) par_override = 0; } if (cd && cd->cs_per_word) { @@ -1176,16 +1207,12 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, m->actual_length = 0; m->status = 0; - /* reject invalid messages and transfers */ - if (list_empty(&m->transfers)) - return -EINVAL; list_for_each_entry(t, &m->transfers, transfer_list) { const void *tx_buf = t->tx_buf; void *rx_buf = t->rx_buf; unsigned len = t->len; - if (t->speed_hz > OMAP2_MCSPI_MAX_FREQ - || (len && !(rx_buf || tx_buf))) { + if ((len && !(rx_buf || tx_buf))) { dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", t->speed_hz, len, @@ -1194,12 +1221,6 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, t->bits_per_word); return -EINVAL; } - if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) { - dev_dbg(mcspi->dev, "speed_hz %d below minimum %d Hz\n", - t->speed_hz, - OMAP2_MCSPI_MAX_FREQ >> 15); - return -EINVAL; - } if (m->is_dma_mapped || len < DMA_MIN_BYTES) continue; @@ -1311,6 +1332,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->transfer_one_message = omap2_mcspi_transfer_one_message; master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; + master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ; + master->min_speed_hz = OMAP2_MCSPI_MAX_FREQ >> 15; platform_set_drvdata(pdev, master); diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 7f2121fe2622..d018a4aac3a1 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -9,7 +9,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/platform_device.h> @@ -43,8 +42,6 @@ struct orion_spi { struct spi_master *master; void __iomem *base; - unsigned int max_speed; - unsigned int min_speed; struct clk *clk; }; @@ -75,23 +72,6 @@ orion_spi_clrbits(struct orion_spi *orion_spi, u32 reg, u32 mask) writel(val, reg_addr); } -static int orion_spi_set_transfer_size(struct orion_spi *orion_spi, int size) -{ - if (size == 16) { - orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG, - ORION_SPI_IF_8_16_BIT_MODE); - } else if (size == 8) { - orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG, - ORION_SPI_IF_8_16_BIT_MODE); - } else { - pr_debug("Bad bits per word value %d (only 8 or 16 are allowed).\n", - size); - return -EINVAL; - } - - return 0; -} - static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed) { u32 tclk_hz; @@ -170,7 +150,14 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) if (rc) return rc; - return orion_spi_set_transfer_size(orion_spi, bits_per_word); + if (bits_per_word == 16) + orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG, + ORION_SPI_IF_8_16_BIT_MODE); + else + orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG, + ORION_SPI_IF_8_16_BIT_MODE); + + return 0; } static void orion_spi_set_cs(struct orion_spi *orion_spi, int enable) @@ -260,11 +247,9 @@ orion_spi_write_read_16bit(struct spi_device *spi, static unsigned int orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer) { - struct orion_spi *orion_spi; unsigned int count; int word_len; - orion_spi = spi_master_get_devdata(spi->master); word_len = spi->bits_per_word; count = xfer->len; @@ -310,27 +295,6 @@ static int orion_spi_transfer_one_message(struct spi_master *master, goto msg_done; list_for_each_entry(t, &m->transfers, transfer_list) { - /* make sure buffer length is even when working in 16 - * bit mode*/ - if ((t->bits_per_word == 16) && (t->len & 1)) { - dev_err(&spi->dev, - "message rejected : " - "odd data length %d while in 16 bit mode\n", - t->len); - status = -EIO; - goto msg_done; - } - - if (t->speed_hz && t->speed_hz < orion_spi->min_speed) { - dev_err(&spi->dev, - "message rejected : " - "device min speed (%d Hz) exceeds " - "required transfer speed (%d Hz)\n", - orion_spi->min_speed, t->speed_hz); - status = -EIO; - goto msg_done; - } - if (par_override || t->speed_hz || t->bits_per_word) { par_override = 1; status = orion_spi_setup_transfer(spi, t); @@ -375,28 +339,6 @@ static int orion_spi_reset(struct orion_spi *orion_spi) return 0; } -static int orion_spi_setup(struct spi_device *spi) -{ - struct orion_spi *orion_spi; - - orion_spi = spi_master_get_devdata(spi->master); - - if ((spi->max_speed_hz == 0) - || (spi->max_speed_hz > orion_spi->max_speed)) - spi->max_speed_hz = orion_spi->max_speed; - - if (spi->max_speed_hz < orion_spi->min_speed) { - dev_err(&spi->dev, "setup: requested speed too low %d Hz\n", - spi->max_speed_hz); - return -EINVAL; - } - - /* - * baudrate & width will be set orion_spi_setup_transfer - */ - return 0; -} - static int orion_spi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -425,9 +367,9 @@ 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->setup = orion_spi_setup; master->transfer_one_message = orion_spi_transfer_one_message; master->num_chipselect = ORION_NUM_CHIPSELECTS; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); platform_set_drvdata(pdev, master); @@ -443,8 +385,8 @@ static int orion_spi_probe(struct platform_device *pdev) clk_prepare(spi->clk); clk_enable(spi->clk); tclk_hz = clk_get_rate(spi->clk); - spi->max_speed = DIV_ROUND_UP(tclk_hz, 4); - spi->min_speed = DIV_ROUND_UP(tclk_hz, 30); + master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4); + master->min_speed_hz = DIV_ROUND_UP(tclk_hz, 30); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); spi->base = devm_ioremap_resource(&pdev->dev, r); diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 2789b452e711..51d99779682f 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -459,9 +459,8 @@ static void giveback(struct pl022 *pl022) struct spi_transfer *last_transfer; pl022->next_msg_cs_active = false; - last_transfer = list_entry(pl022->cur_msg->transfers.prev, - struct spi_transfer, - transfer_list); + last_transfer = list_last_entry(&pl022->cur_msg->transfers, + struct spi_transfer, transfer_list); /* Delay if requested before any change in chip select */ if (last_transfer->delay_usecs) @@ -2109,8 +2108,6 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int), GFP_KERNEL); - pinctrl_pm_select_default_state(dev); - /* * Bus Number Which has been Assigned to this SSP controller * on this board @@ -2183,13 +2180,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) goto err_no_clk; } - status = clk_prepare(pl022->clk); - if (status) { - dev_err(&adev->dev, "could not prepare SSP/SPI bus clock\n"); - goto err_clk_prep; - } - - status = clk_enable(pl022->clk); + status = clk_prepare_enable(pl022->clk); if (status) { dev_err(&adev->dev, "could not enable SSP/SPI bus clock\n"); goto err_no_clk_en; @@ -2250,10 +2241,8 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) if (platform_info->enable_dma) pl022_dma_remove(pl022); err_no_irq: - clk_disable(pl022->clk); + clk_disable_unprepare(pl022->clk); err_no_clk_en: - clk_unprepare(pl022->clk); - err_clk_prep: err_no_clk: err_no_ioremap: amba_release_regions(adev); @@ -2281,42 +2270,13 @@ pl022_remove(struct amba_device *adev) if (pl022->master_info->enable_dma) pl022_dma_remove(pl022); - clk_disable(pl022->clk); - clk_unprepare(pl022->clk); + clk_disable_unprepare(pl022->clk); amba_release_regions(adev); tasklet_disable(&pl022->pump_transfers); return 0; } -#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME) -/* - * These two functions are used from both suspend/resume and - * the runtime counterparts to handle external resources like - * clocks, pins and regulators when going to sleep. - */ -static void pl022_suspend_resources(struct pl022 *pl022, bool runtime) -{ - clk_disable(pl022->clk); - - if (runtime) - pinctrl_pm_select_idle_state(&pl022->adev->dev); - else - pinctrl_pm_select_sleep_state(&pl022->adev->dev); -} - -static void pl022_resume_resources(struct pl022 *pl022, bool runtime) -{ - /* First go to the default state */ - pinctrl_pm_select_default_state(&pl022->adev->dev); - if (!runtime) - /* Then let's idle the pins until the next transfer happens */ - pinctrl_pm_select_idle_state(&pl022->adev->dev); - - clk_enable(pl022->clk); -} -#endif - -#ifdef CONFIG_SUSPEND +#ifdef CONFIG_PM_SLEEP static int pl022_suspend(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); @@ -2328,8 +2288,13 @@ static int pl022_suspend(struct device *dev) return ret; } - pm_runtime_get_sync(dev); - pl022_suspend_resources(pl022, false); + ret = pm_runtime_force_suspend(dev); + if (ret) { + spi_master_resume(pl022->master); + return ret; + } + + pinctrl_pm_select_sleep_state(dev); dev_dbg(dev, "suspended\n"); return 0; @@ -2340,8 +2305,9 @@ static int pl022_resume(struct device *dev) struct pl022 *pl022 = dev_get_drvdata(dev); int ret; - pl022_resume_resources(pl022, false); - pm_runtime_put(dev); + ret = pm_runtime_force_resume(dev); + if (ret) + dev_err(dev, "problem resuming\n"); /* Start the queue running */ ret = spi_master_resume(pl022->master); @@ -2352,14 +2318,16 @@ static int pl022_resume(struct device *dev) return ret; } -#endif /* CONFIG_PM */ +#endif -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM static int pl022_runtime_suspend(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - pl022_suspend_resources(pl022, true); + clk_disable_unprepare(pl022->clk); + pinctrl_pm_select_idle_state(dev); + return 0; } @@ -2367,14 +2335,16 @@ static int pl022_runtime_resume(struct device *dev) { struct pl022 *pl022 = dev_get_drvdata(dev); - pl022_resume_resources(pl022, true); + pinctrl_pm_select_default_state(dev); + clk_prepare_enable(pl022->clk); + return 0; } #endif static const struct dev_pm_ops pl022_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume) - SET_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL) + SET_PM_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL) }; static struct vendor_data vendor_arm = { diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 5ee56726f8d0..80b8408ac3e3 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -24,7 +24,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/errno.h> diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index 3c0b55125f1e..713af4806f26 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -9,7 +9,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> diff --git a/drivers/spi/spi-pxa2xx-pxadma.c b/drivers/spi/spi-pxa2xx-pxadma.c index 2916efc7cfe5..e8a26f25d5c0 100644 --- a/drivers/spi/spi-pxa2xx-pxadma.c +++ b/drivers/spi/spi-pxa2xx-pxadma.c @@ -18,7 +18,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/init.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/dma-mapping.h> diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index c702fc536a77..41185d0557fa 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -362,8 +362,7 @@ static void giveback(struct driver_data *drv_data) drv_data->cur_msg = NULL; drv_data->cur_transfer = NULL; - last_transfer = list_entry(msg->transfers.prev, - struct spi_transfer, + last_transfer = list_last_entry(&msg->transfers, struct spi_transfer, transfer_list); /* Delay if requested before any change in chip select */ diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c new file mode 100644 index 000000000000..b032e8885e24 --- /dev/null +++ b/drivers/spi/spi-qup.c @@ -0,0 +1,779 @@ +/* + * Copyright (c) 2008-2014, The Linux foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License rev 2 and + * only rev 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/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/spi/spi.h> + +#define QUP_CONFIG 0x0000 +#define QUP_STATE 0x0004 +#define QUP_IO_M_MODES 0x0008 +#define QUP_SW_RESET 0x000c +#define QUP_OPERATIONAL 0x0018 +#define QUP_ERROR_FLAGS 0x001c +#define QUP_ERROR_FLAGS_EN 0x0020 +#define QUP_OPERATIONAL_MASK 0x0028 +#define QUP_HW_VERSION 0x0030 +#define QUP_MX_OUTPUT_CNT 0x0100 +#define QUP_OUTPUT_FIFO 0x0110 +#define QUP_MX_WRITE_CNT 0x0150 +#define QUP_MX_INPUT_CNT 0x0200 +#define QUP_MX_READ_CNT 0x0208 +#define QUP_INPUT_FIFO 0x0218 + +#define SPI_CONFIG 0x0300 +#define SPI_IO_CONTROL 0x0304 +#define SPI_ERROR_FLAGS 0x0308 +#define SPI_ERROR_FLAGS_EN 0x030c + +/* QUP_CONFIG fields */ +#define QUP_CONFIG_SPI_MODE (1 << 8) +#define QUP_CONFIG_CLOCK_AUTO_GATE BIT(13) +#define QUP_CONFIG_NO_INPUT BIT(7) +#define QUP_CONFIG_NO_OUTPUT BIT(6) +#define QUP_CONFIG_N 0x001f + +/* QUP_STATE fields */ +#define QUP_STATE_VALID BIT(2) +#define QUP_STATE_RESET 0 +#define QUP_STATE_RUN 1 +#define QUP_STATE_PAUSE 3 +#define QUP_STATE_MASK 3 +#define QUP_STATE_CLEAR 2 + +#define QUP_HW_VERSION_2_1_1 0x20010001 + +/* QUP_IO_M_MODES fields */ +#define QUP_IO_M_PACK_EN BIT(15) +#define QUP_IO_M_UNPACK_EN BIT(14) +#define QUP_IO_M_INPUT_MODE_MASK_SHIFT 12 +#define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT 10 +#define QUP_IO_M_INPUT_MODE_MASK (3 << QUP_IO_M_INPUT_MODE_MASK_SHIFT) +#define QUP_IO_M_OUTPUT_MODE_MASK (3 << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT) + +#define QUP_IO_M_OUTPUT_BLOCK_SIZE(x) (((x) & (0x03 << 0)) >> 0) +#define QUP_IO_M_OUTPUT_FIFO_SIZE(x) (((x) & (0x07 << 2)) >> 2) +#define QUP_IO_M_INPUT_BLOCK_SIZE(x) (((x) & (0x03 << 5)) >> 5) +#define QUP_IO_M_INPUT_FIFO_SIZE(x) (((x) & (0x07 << 7)) >> 7) + +#define QUP_IO_M_MODE_FIFO 0 +#define QUP_IO_M_MODE_BLOCK 1 +#define QUP_IO_M_MODE_DMOV 2 +#define QUP_IO_M_MODE_BAM 3 + +/* QUP_OPERATIONAL fields */ +#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) +#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) +#define QUP_OP_IN_SERVICE_FLAG BIT(9) +#define QUP_OP_OUT_SERVICE_FLAG BIT(8) +#define QUP_OP_IN_FIFO_FULL BIT(7) +#define QUP_OP_OUT_FIFO_FULL BIT(6) +#define QUP_OP_IN_FIFO_NOT_EMPTY BIT(5) +#define QUP_OP_OUT_FIFO_NOT_EMPTY BIT(4) + +/* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */ +#define QUP_ERROR_OUTPUT_OVER_RUN BIT(5) +#define QUP_ERROR_INPUT_UNDER_RUN BIT(4) +#define QUP_ERROR_OUTPUT_UNDER_RUN BIT(3) +#define QUP_ERROR_INPUT_OVER_RUN BIT(2) + +/* SPI_CONFIG fields */ +#define SPI_CONFIG_HS_MODE BIT(10) +#define SPI_CONFIG_INPUT_FIRST BIT(9) +#define SPI_CONFIG_LOOPBACK BIT(8) + +/* SPI_IO_CONTROL fields */ +#define SPI_IO_C_FORCE_CS BIT(11) +#define SPI_IO_C_CLK_IDLE_HIGH BIT(10) +#define SPI_IO_C_MX_CS_MODE BIT(8) +#define SPI_IO_C_CS_N_POLARITY_0 BIT(4) +#define SPI_IO_C_CS_SELECT(x) (((x) & 3) << 2) +#define SPI_IO_C_CS_SELECT_MASK 0x000c +#define SPI_IO_C_TRISTATE_CS BIT(1) +#define SPI_IO_C_NO_TRI_STATE BIT(0) + +/* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */ +#define SPI_ERROR_CLK_OVER_RUN BIT(1) +#define SPI_ERROR_CLK_UNDER_RUN BIT(0) + +#define SPI_NUM_CHIPSELECTS 4 + +/* high speed mode is when bus rate is greater then 26MHz */ +#define SPI_HS_MIN_RATE 26000000 +#define SPI_MAX_RATE 50000000 + +#define SPI_DELAY_THRESHOLD 1 +#define SPI_DELAY_RETRY 10 + +struct spi_qup { + void __iomem *base; + struct device *dev; + struct clk *cclk; /* core clock */ + struct clk *iclk; /* interface clock */ + int irq; + spinlock_t lock; + + int in_fifo_sz; + int out_fifo_sz; + int in_blk_sz; + int out_blk_sz; + + struct spi_transfer *xfer; + struct completion done; + int error; + int w_size; /* bytes per SPI word */ + int tx_bytes; + int rx_bytes; +}; + + +static inline bool spi_qup_is_valid_state(struct spi_qup *controller) +{ + u32 opstate = readl_relaxed(controller->base + QUP_STATE); + + return opstate & QUP_STATE_VALID; +} + +static int spi_qup_set_state(struct spi_qup *controller, u32 state) +{ + unsigned long loop; + u32 cur_state; + + loop = 0; + while (!spi_qup_is_valid_state(controller)) { + + usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2); + + if (++loop > SPI_DELAY_RETRY) + return -EIO; + } + + if (loop) + dev_dbg(controller->dev, "invalid state for %ld,us %d\n", + loop, state); + + cur_state = readl_relaxed(controller->base + QUP_STATE); + /* + * Per spec: for PAUSE_STATE to RESET_STATE, two writes + * of (b10) are required + */ + if (((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE) && + (state == QUP_STATE_RESET)) { + writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE); + writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE); + } else { + cur_state &= ~QUP_STATE_MASK; + cur_state |= state; + writel_relaxed(cur_state, controller->base + QUP_STATE); + } + + loop = 0; + while (!spi_qup_is_valid_state(controller)) { + + usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2); + + if (++loop > SPI_DELAY_RETRY) + return -EIO; + } + + return 0; +} + + +static void spi_qup_fifo_read(struct spi_qup *controller, + struct spi_transfer *xfer) +{ + u8 *rx_buf = xfer->rx_buf; + u32 word, state; + int idx, shift, w_size; + + w_size = controller->w_size; + + while (controller->rx_bytes < xfer->len) { + + state = readl_relaxed(controller->base + QUP_OPERATIONAL); + if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY)) + break; + + word = readl_relaxed(controller->base + QUP_INPUT_FIFO); + + if (!rx_buf) { + controller->rx_bytes += w_size; + continue; + } + + for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) { + /* + * The data format depends on bytes per SPI word: + * 4 bytes: 0x12345678 + * 2 bytes: 0x00001234 + * 1 byte : 0x00000012 + */ + shift = BITS_PER_BYTE; + shift *= (w_size - idx - 1); + rx_buf[controller->rx_bytes] = word >> shift; + } + } +} + +static void spi_qup_fifo_write(struct spi_qup *controller, + struct spi_transfer *xfer) +{ + const u8 *tx_buf = xfer->tx_buf; + u32 word, state, data; + int idx, w_size; + + w_size = controller->w_size; + + while (controller->tx_bytes < xfer->len) { + + state = readl_relaxed(controller->base + QUP_OPERATIONAL); + if (state & QUP_OP_OUT_FIFO_FULL) + break; + + word = 0; + for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) { + + if (!tx_buf) { + controller->tx_bytes += w_size; + break; + } + + data = tx_buf[controller->tx_bytes]; + word |= data << (BITS_PER_BYTE * (3 - idx)); + } + + writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); + } +} + +static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) +{ + struct spi_qup *controller = dev_id; + struct spi_transfer *xfer; + u32 opflags, qup_err, spi_err; + unsigned long flags; + int error = 0; + + spin_lock_irqsave(&controller->lock, flags); + xfer = controller->xfer; + controller->xfer = NULL; + spin_unlock_irqrestore(&controller->lock, flags); + + qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS); + spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS); + opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); + + writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); + writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); + writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); + + if (!xfer) { + dev_err_ratelimited(controller->dev, "unexpected irq %x08 %x08 %x08\n", + qup_err, spi_err, opflags); + return IRQ_HANDLED; + } + + if (qup_err) { + if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN) + dev_warn(controller->dev, "OUTPUT_OVER_RUN\n"); + if (qup_err & QUP_ERROR_INPUT_UNDER_RUN) + dev_warn(controller->dev, "INPUT_UNDER_RUN\n"); + if (qup_err & QUP_ERROR_OUTPUT_UNDER_RUN) + dev_warn(controller->dev, "OUTPUT_UNDER_RUN\n"); + if (qup_err & QUP_ERROR_INPUT_OVER_RUN) + dev_warn(controller->dev, "INPUT_OVER_RUN\n"); + + error = -EIO; + } + + if (spi_err) { + if (spi_err & SPI_ERROR_CLK_OVER_RUN) + dev_warn(controller->dev, "CLK_OVER_RUN\n"); + if (spi_err & SPI_ERROR_CLK_UNDER_RUN) + dev_warn(controller->dev, "CLK_UNDER_RUN\n"); + + error = -EIO; + } + + if (opflags & QUP_OP_IN_SERVICE_FLAG) + spi_qup_fifo_read(controller, xfer); + + if (opflags & QUP_OP_OUT_SERVICE_FLAG) + spi_qup_fifo_write(controller, xfer); + + spin_lock_irqsave(&controller->lock, flags); + controller->error = error; + controller->xfer = xfer; + spin_unlock_irqrestore(&controller->lock, flags); + + if (controller->rx_bytes == xfer->len || error) + complete(&controller->done); + + return IRQ_HANDLED; +} + + +/* set clock freq ... bits per word */ +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; + int ret, n_words, w_size; + + if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { + dev_err(controller->dev, "too big size for loopback %d > %d\n", + xfer->len, controller->in_fifo_sz); + return -EIO; + } + + ret = clk_set_rate(controller->cclk, xfer->speed_hz); + if (ret) { + dev_err(controller->dev, "fail to set frequency %d", + xfer->speed_hz); + return -EIO; + } + + if (spi_qup_set_state(controller, QUP_STATE_RESET)) { + dev_err(controller->dev, "cannot set RESET state\n"); + return -EIO; + } + + w_size = 4; + if (xfer->bits_per_word <= 8) + w_size = 1; + else if (xfer->bits_per_word <= 16) + w_size = 2; + + n_words = xfer->len / w_size; + controller->w_size = w_size; + + if (n_words <= controller->in_fifo_sz) { + mode = QUP_IO_M_MODE_FIFO; + writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT); + /* must be zero for FIFO */ + writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); + } else { + mode = QUP_IO_M_MODE_BLOCK; + writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); + /* must be zero for BLOCK and BAM */ + writel_relaxed(0, controller->base + QUP_MX_READ_CNT); + writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); + } + + iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); + /* Set input and output transfer mode */ + iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); + iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); + iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); + iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); + + writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); + + config = readl_relaxed(controller->base + SPI_CONFIG); + + if (spi->mode & SPI_LOOP) + config |= SPI_CONFIG_LOOPBACK; + else + config &= ~SPI_CONFIG_LOOPBACK; + + if (spi->mode & SPI_CPHA) + config &= ~SPI_CONFIG_INPUT_FIRST; + else + config |= SPI_CONFIG_INPUT_FIRST; + + /* + * HS_MODE improves signal stability for spi-clk high rates, + * but is invalid in loop back mode. + */ + if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP)) + config |= SPI_CONFIG_HS_MODE; + else + config &= ~SPI_CONFIG_HS_MODE; + + writel_relaxed(config, controller->base + SPI_CONFIG); + + config = readl_relaxed(controller->base + QUP_CONFIG); + config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); + config |= xfer->bits_per_word - 1; + config |= QUP_CONFIG_SPI_MODE; + writel_relaxed(config, controller->base + QUP_CONFIG); + + writel_relaxed(0, controller->base + QUP_OPERATIONAL_MASK); + return 0; +} + +static void spi_qup_set_cs(struct spi_device *spi, bool enable) +{ + struct spi_qup *controller = spi_master_get_devdata(spi->master); + + u32 iocontol, mask; + + iocontol = readl_relaxed(controller->base + SPI_IO_CONTROL); + + /* Disable auto CS toggle and use manual */ + iocontol &= ~SPI_IO_C_MX_CS_MODE; + iocontol |= SPI_IO_C_FORCE_CS; + + iocontol &= ~SPI_IO_C_CS_SELECT_MASK; + iocontol |= SPI_IO_C_CS_SELECT(spi->chip_select); + + mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select; + + if (enable) + iocontol |= mask; + else + iocontol &= ~mask; + + writel_relaxed(iocontol, controller->base + SPI_IO_CONTROL); +} + +static int spi_qup_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct spi_qup *controller = spi_master_get_devdata(master); + unsigned long timeout, flags; + int ret = -EIO; + + ret = spi_qup_io_config(spi, xfer); + if (ret) + return ret; + + timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC); + timeout = DIV_ROUND_UP(xfer->len * 8, timeout); + timeout = 100 * msecs_to_jiffies(timeout); + + reinit_completion(&controller->done); + + spin_lock_irqsave(&controller->lock, flags); + controller->xfer = xfer; + controller->error = 0; + controller->rx_bytes = 0; + controller->tx_bytes = 0; + spin_unlock_irqrestore(&controller->lock, flags); + + if (spi_qup_set_state(controller, QUP_STATE_RUN)) { + dev_warn(controller->dev, "cannot set RUN state\n"); + goto exit; + } + + if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) { + dev_warn(controller->dev, "cannot set PAUSE state\n"); + goto exit; + } + + spi_qup_fifo_write(controller, xfer); + + if (spi_qup_set_state(controller, QUP_STATE_RUN)) { + dev_warn(controller->dev, "cannot set EXECUTE state\n"); + goto exit; + } + + if (!wait_for_completion_timeout(&controller->done, timeout)) + ret = -ETIMEDOUT; +exit: + spi_qup_set_state(controller, QUP_STATE_RESET); + spin_lock_irqsave(&controller->lock, flags); + controller->xfer = NULL; + if (!ret) + ret = controller->error; + spin_unlock_irqrestore(&controller->lock, flags); + return ret; +} + +static int spi_qup_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct clk *iclk, *cclk; + struct spi_qup *controller; + struct resource *res; + struct device *dev; + void __iomem *base; + u32 data, max_freq, iomode; + int ret, irq, size; + + dev = &pdev->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + cclk = devm_clk_get(dev, "core"); + if (IS_ERR(cclk)) + return PTR_ERR(cclk); + + iclk = devm_clk_get(dev, "iface"); + if (IS_ERR(iclk)) + return PTR_ERR(iclk); + + /* This is optional parameter */ + if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq)) + max_freq = SPI_MAX_RATE; + + if (!max_freq || max_freq > SPI_MAX_RATE) { + dev_err(dev, "invalid clock frequency %d\n", max_freq); + return -ENXIO; + } + + ret = clk_prepare_enable(cclk); + if (ret) { + dev_err(dev, "cannot enable core clock\n"); + return ret; + } + + ret = clk_prepare_enable(iclk); + if (ret) { + clk_disable_unprepare(cclk); + dev_err(dev, "cannot enable iface clock\n"); + return ret; + } + + data = readl_relaxed(base + QUP_HW_VERSION); + + if (data < QUP_HW_VERSION_2_1_1) { + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); + dev_err(dev, "v.%08x is not supported\n", data); + return -ENXIO; + } + + master = spi_alloc_master(dev, sizeof(struct spi_qup)); + if (!master) { + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); + dev_err(dev, "cannot allocate master\n"); + return -ENOMEM; + } + + master->bus_num = pdev->id; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; + master->num_chipselect = SPI_NUM_CHIPSELECTS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + master->max_speed_hz = max_freq; + master->set_cs = spi_qup_set_cs; + master->transfer_one = spi_qup_transfer_one; + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; + + platform_set_drvdata(pdev, master); + + controller = spi_master_get_devdata(master); + + controller->dev = dev; + controller->base = base; + controller->iclk = iclk; + controller->cclk = cclk; + controller->irq = irq; + + spin_lock_init(&controller->lock); + init_completion(&controller->done); + + iomode = readl_relaxed(base + QUP_IO_M_MODES); + + size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode); + if (size) + controller->out_blk_sz = size * 16; + else + controller->out_blk_sz = 4; + + size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode); + if (size) + controller->in_blk_sz = size * 16; + else + controller->in_blk_sz = 4; + + size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode); + controller->out_fifo_sz = controller->out_blk_sz * (2 << size); + + size = QUP_IO_M_INPUT_FIFO_SIZE(iomode); + controller->in_fifo_sz = controller->in_blk_sz * (2 << size); + + dev_info(dev, "v.%08x IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", + data, controller->in_blk_sz, controller->in_fifo_sz, + controller->out_blk_sz, controller->out_fifo_sz); + + writel_relaxed(1, base + QUP_SW_RESET); + + ret = spi_qup_set_state(controller, QUP_STATE_RESET); + if (ret) { + dev_err(dev, "cannot set RESET state\n"); + goto error; + } + + writel_relaxed(0, base + QUP_OPERATIONAL); + writel_relaxed(0, base + QUP_IO_M_MODES); + writel_relaxed(0, base + QUP_OPERATIONAL_MASK); + writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN, + base + SPI_ERROR_FLAGS_EN); + + writel_relaxed(0, base + SPI_CONFIG); + writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL); + + ret = devm_request_irq(dev, irq, spi_qup_qup_irq, + IRQF_TRIGGER_HIGH, pdev->name, controller); + if (ret) + goto error; + + ret = devm_spi_register_master(dev, master); + if (ret) + goto error; + + pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + return 0; + +error: + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); + spi_master_put(master); + return ret; +} + +#ifdef CONFIG_PM_RUNTIME +static int spi_qup_pm_suspend_runtime(struct device *device) +{ + struct spi_master *master = dev_get_drvdata(device); + struct spi_qup *controller = spi_master_get_devdata(master); + u32 config; + + /* Enable clocks auto gaiting */ + config = readl(controller->base + QUP_CONFIG); + config |= QUP_CONFIG_CLOCK_AUTO_GATE; + writel_relaxed(config, controller->base + QUP_CONFIG); + return 0; +} + +static int spi_qup_pm_resume_runtime(struct device *device) +{ + struct spi_master *master = dev_get_drvdata(device); + struct spi_qup *controller = spi_master_get_devdata(master); + u32 config; + + /* Disable clocks auto gaiting */ + config = readl_relaxed(controller->base + QUP_CONFIG); + config &= ~QUP_CONFIG_CLOCK_AUTO_GATE; + writel_relaxed(config, controller->base + QUP_CONFIG); + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_SLEEP +static int spi_qup_suspend(struct device *device) +{ + struct spi_master *master = dev_get_drvdata(device); + struct spi_qup *controller = spi_master_get_devdata(master); + int ret; + + ret = spi_master_suspend(master); + if (ret) + return ret; + + ret = spi_qup_set_state(controller, QUP_STATE_RESET); + if (ret) + return ret; + + clk_disable_unprepare(controller->cclk); + clk_disable_unprepare(controller->iclk); + return 0; +} + +static int spi_qup_resume(struct device *device) +{ + struct spi_master *master = dev_get_drvdata(device); + struct spi_qup *controller = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(controller->iclk); + if (ret) + return ret; + + ret = clk_prepare_enable(controller->cclk); + if (ret) + return ret; + + ret = spi_qup_set_state(controller, QUP_STATE_RESET); + if (ret) + return ret; + + return spi_master_resume(master); +} +#endif /* CONFIG_PM_SLEEP */ + +static int spi_qup_remove(struct platform_device *pdev) +{ + struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct spi_qup *controller = spi_master_get_devdata(master); + int ret; + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret) + return ret; + + ret = spi_qup_set_state(controller, QUP_STATE_RESET); + if (ret) + return ret; + + clk_disable_unprepare(controller->cclk); + clk_disable_unprepare(controller->iclk); + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return 0; +} + +static struct of_device_id spi_qup_dt_match[] = { + { .compatible = "qcom,spi-qup-v2.1.1", }, + { .compatible = "qcom,spi-qup-v2.2.1", }, + { } +}; +MODULE_DEVICE_TABLE(of, spi_qup_dt_match); + +static const struct dev_pm_ops spi_qup_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume) + SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime, + spi_qup_pm_resume_runtime, + NULL) +}; + +static struct platform_driver spi_qup_driver = { + .driver = { + .name = "spi_qup", + .owner = THIS_MODULE, + .pm = &spi_qup_dev_pm_ops, + .of_match_table = spi_qup_dt_match, + }, + .probe = spi_qup_probe, + .remove = spi_qup_remove, +}; +module_platform_driver(spi_qup_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:spi_qup"); diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 28987d9fcfe5..1fb0ad213324 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1,7 +1,8 @@ /* * SH RSPI driver * - * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012, 2013 Renesas Solutions Corp. + * Copyright (C) 2014 Glider bvba * * Based on spi-sh.c: * Copyright (C) 2011 Renesas Solutions Corp. @@ -25,14 +26,14 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/errno.h> -#include <linux/list.h> -#include <linux/workqueue.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/clk.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> +#include <linux/of_device.h> +#include <linux/pm_runtime.h> #include <linux/sh_dma.h> #include <linux/spi/spi.h> #include <linux/spi/rspi.h> @@ -49,7 +50,7 @@ #define RSPI_SPCKD 0x0c /* Clock Delay Register */ #define RSPI_SSLND 0x0d /* Slave Select Negation Delay Register */ #define RSPI_SPND 0x0e /* Next-Access Delay Register */ -#define RSPI_SPCR2 0x0f /* Control Register 2 */ +#define RSPI_SPCR2 0x0f /* Control Register 2 (SH only) */ #define RSPI_SPCMD0 0x10 /* Command Register 0 */ #define RSPI_SPCMD1 0x12 /* Command Register 1 */ #define RSPI_SPCMD2 0x14 /* Command Register 2 */ @@ -58,16 +59,23 @@ #define RSPI_SPCMD5 0x1a /* Command Register 5 */ #define RSPI_SPCMD6 0x1c /* Command Register 6 */ #define RSPI_SPCMD7 0x1e /* Command Register 7 */ +#define RSPI_SPCMD(i) (RSPI_SPCMD0 + (i) * 2) +#define RSPI_NUM_SPCMD 8 +#define RSPI_RZ_NUM_SPCMD 4 +#define QSPI_NUM_SPCMD 4 + +/* RSPI on RZ only */ #define RSPI_SPBFCR 0x20 /* Buffer Control Register */ #define RSPI_SPBFDR 0x22 /* Buffer Data Count Setting Register */ -/*qspi only */ +/* QSPI only */ #define QSPI_SPBFCR 0x18 /* Buffer Control Register */ #define QSPI_SPBDCR 0x1a /* Buffer Data Count Register */ #define QSPI_SPBMUL0 0x1c /* Transfer Data Length Multiplier Setting Register 0 */ #define QSPI_SPBMUL1 0x20 /* Transfer Data Length Multiplier Setting Register 1 */ #define QSPI_SPBMUL2 0x24 /* Transfer Data Length Multiplier Setting Register 2 */ #define QSPI_SPBMUL3 0x28 /* Transfer Data Length Multiplier Setting Register 3 */ +#define QSPI_SPBMUL(i) (QSPI_SPBMUL0 + (i) * 4) /* SPCR - Control Register */ #define SPCR_SPRIE 0x80 /* Receive Interrupt Enable */ @@ -104,7 +112,7 @@ #define SPSR_PERF 0x08 /* Parity Error Flag */ #define SPSR_MODF 0x04 /* Mode Fault Error Flag */ #define SPSR_IDLNF 0x02 /* RSPI Idle Flag */ -#define SPSR_OVRF 0x01 /* Overrun Error Flag */ +#define SPSR_OVRF 0x01 /* Overrun Error Flag (RSPI only) */ /* SPSCR - Sequence Control Register */ #define SPSCR_SPSLN_MASK 0x07 /* Sequence Length Specification */ @@ -121,13 +129,13 @@ #define SPDCR_SPLWORD SPDCR_SPLW1 #define SPDCR_SPLBYTE SPDCR_SPLW0 #define SPDCR_SPLW 0x20 /* Access Width Specification (SH) */ -#define SPDCR_SPRDTD 0x10 /* Receive Transmit Data Select */ +#define SPDCR_SPRDTD 0x10 /* Receive Transmit Data Select (SH) */ #define SPDCR_SLSEL1 0x08 #define SPDCR_SLSEL0 0x04 -#define SPDCR_SLSEL_MASK 0x0c /* SSL1 Output Select */ +#define SPDCR_SLSEL_MASK 0x0c /* SSL1 Output Select (SH) */ #define SPDCR_SPFC1 0x02 #define SPDCR_SPFC0 0x01 -#define SPDCR_SPFC_MASK 0x03 /* Frame Count Setting (1-4) */ +#define SPDCR_SPFC_MASK 0x03 /* Frame Count Setting (1-4) (SH) */ /* SPCKD - Clock Delay Register */ #define SPCKD_SCKDL_MASK 0x07 /* Clock Delay Setting (1-8) */ @@ -151,7 +159,7 @@ #define SPCMD_LSBF 0x1000 /* LSB First */ #define SPCMD_SPB_MASK 0x0f00 /* Data Length Setting */ #define SPCMD_SPB_8_TO_16(bit) (((bit - 1) << 8) & SPCMD_SPB_MASK) -#define SPCMD_SPB_8BIT 0x0000 /* qspi only */ +#define SPCMD_SPB_8BIT 0x0000 /* QSPI only */ #define SPCMD_SPB_16BIT 0x0100 #define SPCMD_SPB_20BIT 0x0000 #define SPCMD_SPB_24BIT 0x0100 @@ -170,8 +178,8 @@ #define SPCMD_CPHA 0x0001 /* Clock Phase Setting */ /* SPBFCR - Buffer Control Register */ -#define SPBFCR_TXRST 0x80 /* Transmit Buffer Data Reset (qspi only) */ -#define SPBFCR_RXRST 0x40 /* Receive Buffer Data Reset (qspi only) */ +#define SPBFCR_TXRST 0x80 /* Transmit Buffer Data Reset */ +#define SPBFCR_RXRST 0x40 /* Receive Buffer Data Reset */ #define SPBFCR_TXTRG_MASK 0x30 /* Transmit Buffer Data Triggering Number */ #define SPBFCR_RXTRG_MASK 0x07 /* Receive Buffer Data Triggering Number */ @@ -181,22 +189,21 @@ struct rspi_data { void __iomem *addr; u32 max_speed_hz; struct spi_master *master; - struct list_head queue; - struct work_struct ws; wait_queue_head_t wait; - spinlock_t lock; struct clk *clk; - u8 spsr; u16 spcmd; + u8 spsr; + u8 sppcr; + int rx_irq, tx_irq; const struct spi_ops *ops; /* for dmaengine */ struct dma_chan *chan_tx; struct dma_chan *chan_rx; - int irq; unsigned dma_width_16bit:1; unsigned dma_callbacked:1; + unsigned byte_access:1; }; static void rspi_write8(const struct rspi_data *rspi, u8 data, u16 offset) @@ -224,34 +231,47 @@ static u16 rspi_read16(const struct rspi_data *rspi, u16 offset) return ioread16(rspi->addr + offset); } +static void rspi_write_data(const struct rspi_data *rspi, u16 data) +{ + if (rspi->byte_access) + rspi_write8(rspi, data, RSPI_SPDR); + else /* 16 bit */ + rspi_write16(rspi, data, RSPI_SPDR); +} + +static u16 rspi_read_data(const struct rspi_data *rspi) +{ + if (rspi->byte_access) + return rspi_read8(rspi, RSPI_SPDR); + else /* 16 bit */ + return rspi_read16(rspi, RSPI_SPDR); +} + /* optional functions */ struct spi_ops { - int (*set_config_register)(const struct rspi_data *rspi, - int access_size); - int (*send_pio)(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t); - int (*receive_pio)(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t); - + int (*set_config_register)(struct rspi_data *rspi, int access_size); + int (*transfer_one)(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer); + u16 mode_bits; }; /* - * functions for RSPI + * functions for RSPI on legacy SH */ -static int rspi_set_config_register(const struct rspi_data *rspi, - int access_size) +static int rspi_set_config_register(struct rspi_data *rspi, int access_size) { int spbr; - /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */ - rspi_write8(rspi, 0x00, RSPI_SPPCR); + /* Sets output mode, MOSI signal, and (optionally) loopback */ + rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); /* Sets transfer bit rate */ spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); - /* Sets number of frames to be used: 1 frame */ - rspi_write8(rspi, 0x00, RSPI_SPDCR); + /* Disable dummy transmission, set 16-bit word access, 1 frame */ + rspi_write8(rspi, 0, RSPI_SPDCR); + rspi->byte_access = 0; /* Sets RSPCK, SSL, next-access delay value */ rspi_write8(rspi, 0x00, RSPI_SPCKD); @@ -262,8 +282,41 @@ static int rspi_set_config_register(const struct rspi_data *rspi, rspi_write8(rspi, 0x00, RSPI_SPCR2); /* Sets SPCMD */ - rspi_write16(rspi, SPCMD_SPB_8_TO_16(access_size) | rspi->spcmd, - RSPI_SPCMD0); + rspi->spcmd |= SPCMD_SPB_8_TO_16(access_size); + rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); + + /* Sets RSPI mode */ + rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR); + + return 0; +} + +/* + * functions for RSPI on RZ + */ +static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) +{ + int spbr; + + /* Sets output mode, MOSI signal, and (optionally) loopback */ + rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); + + /* Sets transfer bit rate */ + spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz) - 1; + rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); + + /* Disable dummy transmission, set byte access */ + rspi_write8(rspi, SPDCR_SPLBYTE, RSPI_SPDCR); + rspi->byte_access = 1; + + /* Sets RSPCK, SSL, next-access delay value */ + rspi_write8(rspi, 0x00, RSPI_SPCKD); + rspi_write8(rspi, 0x00, RSPI_SSLND); + rspi_write8(rspi, 0x00, RSPI_SPND); + + /* Sets SPCMD */ + rspi->spcmd |= SPCMD_SPB_8_TO_16(access_size); + rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); /* Sets RSPI mode */ rspi_write8(rspi, SPCR_MSTR, RSPI_SPCR); @@ -274,21 +327,20 @@ static int rspi_set_config_register(const struct rspi_data *rspi, /* * functions for QSPI */ -static int qspi_set_config_register(const struct rspi_data *rspi, - int access_size) +static int qspi_set_config_register(struct rspi_data *rspi, int access_size) { - u16 spcmd; int spbr; - /* Sets output mode(CMOS) and MOSI signal(from previous transfer) */ - rspi_write8(rspi, 0x00, RSPI_SPPCR); + /* Sets output mode, MOSI signal, and (optionally) loopback */ + rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); /* Sets transfer bit rate */ spbr = clk_get_rate(rspi->clk) / (2 * rspi->max_speed_hz); rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); - /* Sets number of frames to be used: 1 frame */ - rspi_write8(rspi, 0x00, RSPI_SPDCR); + /* Disable dummy transmission, set byte access */ + rspi_write8(rspi, 0, RSPI_SPDCR); + rspi->byte_access = 1; /* Sets RSPCK, SSL, next-access delay value */ rspi_write8(rspi, 0x00, RSPI_SPCKD); @@ -297,13 +349,13 @@ static int qspi_set_config_register(const struct rspi_data *rspi, /* Data Length Setting */ if (access_size == 8) - spcmd = SPCMD_SPB_8BIT; + rspi->spcmd |= SPCMD_SPB_8BIT; else if (access_size == 16) - spcmd = SPCMD_SPB_16BIT; + rspi->spcmd |= SPCMD_SPB_16BIT; else - spcmd = SPCMD_SPB_32BIT; + rspi->spcmd |= SPCMD_SPB_32BIT; - spcmd |= SPCMD_SCKDEN | SPCMD_SLNDEN | rspi->spcmd | SPCMD_SPNDEN; + rspi->spcmd |= SPCMD_SCKDEN | SPCMD_SLNDEN | SPCMD_SPNDEN; /* Resets transfer data length */ rspi_write32(rspi, 0, QSPI_SPBMUL0); @@ -314,9 +366,9 @@ static int qspi_set_config_register(const struct rspi_data *rspi, rspi_write8(rspi, 0x00, QSPI_SPBFCR); /* Sets SPCMD */ - rspi_write16(rspi, spcmd, RSPI_SPCMD0); + rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); - /* Enables SPI function in a master mode */ + /* Enables SPI function in master mode */ rspi_write8(rspi, SPCR_SPE | SPCR_MSTR, RSPI_SPCR); return 0; @@ -340,6 +392,9 @@ static int rspi_wait_for_interrupt(struct rspi_data *rspi, u8 wait_mask, int ret; rspi->spsr = rspi_read8(rspi, RSPI_SPSR); + if (rspi->spsr & wait_mask) + return 0; + rspi_enable_irq(rspi, enable_bit); ret = wait_event_timeout(rspi->wait, rspi->spsr & wait_mask, HZ); if (ret == 0 && !(rspi->spsr & wait_mask)) @@ -348,78 +403,39 @@ static int rspi_wait_for_interrupt(struct rspi_data *rspi, u8 wait_mask, return 0; } -static void rspi_assert_ssl(const struct rspi_data *rspi) -{ - rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_SPE, RSPI_SPCR); -} - -static void rspi_negate_ssl(const struct rspi_data *rspi) +static int rspi_data_out(struct rspi_data *rspi, u8 data) { - rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR); + if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { + dev_err(&rspi->master->dev, "transmit timeout\n"); + return -ETIMEDOUT; + } + rspi_write_data(rspi, data); + return 0; } -static int rspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t) +static int rspi_data_in(struct rspi_data *rspi) { - int remain = t->len; - const u8 *data = t->tx_buf; - while (remain > 0) { - rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD, - RSPI_SPCR); - - if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { - dev_err(&rspi->master->dev, - "%s: tx empty timeout\n", __func__); - return -ETIMEDOUT; - } + u8 data; - rspi_write16(rspi, *data, RSPI_SPDR); - data++; - remain--; + if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { + dev_err(&rspi->master->dev, "receive timeout\n"); + return -ETIMEDOUT; } - - /* Waiting for the last transmission */ - rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); - - return 0; + data = rspi_read_data(rspi); + return data; } -static int qspi_send_pio(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t) +static int rspi_data_out_in(struct rspi_data *rspi, u8 data) { - int remain = t->len; - const u8 *data = t->tx_buf; - - rspi_write8(rspi, SPBFCR_TXRST, QSPI_SPBFCR); - rspi_write8(rspi, 0x00, QSPI_SPBFCR); - - while (remain > 0) { - - if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { - dev_err(&rspi->master->dev, - "%s: tx empty timeout\n", __func__); - return -ETIMEDOUT; - } - rspi_write8(rspi, *data++, RSPI_SPDR); - - if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { - dev_err(&rspi->master->dev, - "%s: receive timeout\n", __func__); - return -ETIMEDOUT; - } - rspi_read8(rspi, RSPI_SPDR); - - remain--; - } + int ret; - /* Waiting for the last transmission */ - rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + ret = rspi_data_out(rspi, data); + if (ret < 0) + return ret; - return 0; + return rspi_data_in(rspi); } -#define send_pio(spi, mesg, t) spi->ops->send_pio(spi, mesg, t) - static void rspi_dma_complete(void *arg) { struct rspi_data *rspi = arg; @@ -471,7 +487,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) struct scatterlist sg; const void *buf = NULL; struct dma_async_tx_descriptor *desc; - unsigned len; + unsigned int len; int ret = 0; if (rspi->dma_width_16bit) { @@ -509,7 +525,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) * DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be * called. So, this driver disables the IRQ while DMA transfer. */ - disable_irq(rspi->irq); + disable_irq(rspi->tx_irq); rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_TXMD, RSPI_SPCR); rspi_enable_irq(rspi, SPCR_SPTIE); @@ -528,7 +544,7 @@ static int rspi_send_dma(struct rspi_data *rspi, struct spi_transfer *t) ret = -ETIMEDOUT; rspi_disable_irq(rspi, SPCR_SPTIE); - enable_irq(rspi->irq); + enable_irq(rspi->tx_irq); end: rspi_dma_unmap_sg(&sg, rspi->chan_tx, DMA_TO_DEVICE); @@ -545,46 +561,17 @@ static void rspi_receive_init(const struct rspi_data *rspi) spsr = rspi_read8(rspi, RSPI_SPSR); if (spsr & SPSR_SPRF) - rspi_read16(rspi, RSPI_SPDR); /* dummy read */ + rspi_read_data(rspi); /* dummy read */ if (spsr & SPSR_OVRF) rspi_write8(rspi, rspi_read8(rspi, RSPI_SPSR) & ~SPSR_OVRF, RSPI_SPSR); } -static int rspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t) +static void rspi_rz_receive_init(const struct rspi_data *rspi) { - int remain = t->len; - u8 *data; - rspi_receive_init(rspi); - - data = t->rx_buf; - while (remain > 0) { - rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_TXMD, - RSPI_SPCR); - - if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { - dev_err(&rspi->master->dev, - "%s: tx empty timeout\n", __func__); - return -ETIMEDOUT; - } - /* dummy write for generate clock */ - rspi_write16(rspi, DUMMY_DATA, RSPI_SPDR); - - if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { - dev_err(&rspi->master->dev, - "%s: receive timeout\n", __func__); - return -ETIMEDOUT; - } - /* SPDR allows 16 or 32-bit access only */ - *data = (u8)rspi_read16(rspi, RSPI_SPDR); - - data++; - remain--; - } - - return 0; + rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, RSPI_SPBFCR); + rspi_write8(rspi, 0, RSPI_SPBFCR); } static void qspi_receive_init(const struct rspi_data *rspi) @@ -593,51 +580,17 @@ static void qspi_receive_init(const struct rspi_data *rspi) spsr = rspi_read8(rspi, RSPI_SPSR); if (spsr & SPSR_SPRF) - rspi_read8(rspi, RSPI_SPDR); /* dummy read */ + rspi_read_data(rspi); /* dummy read */ rspi_write8(rspi, SPBFCR_TXRST | SPBFCR_RXRST, QSPI_SPBFCR); - rspi_write8(rspi, 0x00, QSPI_SPBFCR); + rspi_write8(rspi, 0, QSPI_SPBFCR); } -static int qspi_receive_pio(struct rspi_data *rspi, struct spi_message *mesg, - struct spi_transfer *t) -{ - int remain = t->len; - u8 *data; - - qspi_receive_init(rspi); - - data = t->rx_buf; - while (remain > 0) { - - if (rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE) < 0) { - dev_err(&rspi->master->dev, - "%s: tx empty timeout\n", __func__); - return -ETIMEDOUT; - } - /* dummy write for generate clock */ - rspi_write8(rspi, DUMMY_DATA, RSPI_SPDR); - - if (rspi_wait_for_interrupt(rspi, SPSR_SPRF, SPCR_SPRIE) < 0) { - dev_err(&rspi->master->dev, - "%s: receive timeout\n", __func__); - return -ETIMEDOUT; - } - /* SPDR allows 8, 16 or 32-bit access */ - *data++ = rspi_read8(rspi, RSPI_SPDR); - remain--; - } - - return 0; -} - -#define receive_pio(spi, mesg, t) spi->ops->receive_pio(spi, mesg, t) - static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) { struct scatterlist sg, sg_dummy; void *dummy = NULL, *rx_buf = NULL; struct dma_async_tx_descriptor *desc, *desc_dummy; - unsigned len; + unsigned int len; int ret = 0; if (rspi->dma_width_16bit) { @@ -695,7 +648,9 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) * DMAC needs SPTIE, but if SPTIE is set, this IRQ routine will be * called. So, this driver disables the IRQ while DMA transfer. */ - disable_irq(rspi->irq); + disable_irq(rspi->tx_irq); + if (rspi->rx_irq != rspi->tx_irq) + disable_irq(rspi->rx_irq); rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_TXMD, RSPI_SPCR); rspi_enable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE); @@ -718,7 +673,9 @@ static int rspi_receive_dma(struct rspi_data *rspi, struct spi_transfer *t) ret = -ETIMEDOUT; rspi_disable_irq(rspi, SPCR_SPTIE | SPCR_SPRIE); - enable_irq(rspi->irq); + enable_irq(rspi->tx_irq); + if (rspi->rx_irq != rspi->tx_irq) + enable_irq(rspi->rx_irq); end: rspi_dma_unmap_sg(&sg, rspi->chan_rx, DMA_FROM_DEVICE); @@ -746,56 +703,175 @@ static int rspi_is_dma(const struct rspi_data *rspi, struct spi_transfer *t) return 0; } -static void rspi_work(struct work_struct *work) +static int rspi_transfer_out_in(struct rspi_data *rspi, + struct spi_transfer *xfer) { - struct rspi_data *rspi = container_of(work, struct rspi_data, ws); - struct spi_message *mesg; - struct spi_transfer *t; - unsigned long flags; - int ret; + int remain = xfer->len, ret; + const u8 *tx_buf = xfer->tx_buf; + u8 *rx_buf = xfer->rx_buf; + u8 spcr, data; - while (1) { - spin_lock_irqsave(&rspi->lock, flags); - if (list_empty(&rspi->queue)) { - spin_unlock_irqrestore(&rspi->lock, flags); - break; - } - mesg = list_entry(rspi->queue.next, struct spi_message, queue); - list_del_init(&mesg->queue); - spin_unlock_irqrestore(&rspi->lock, flags); - - rspi_assert_ssl(rspi); - - list_for_each_entry(t, &mesg->transfers, transfer_list) { - if (t->tx_buf) { - if (rspi_is_dma(rspi, t)) - ret = rspi_send_dma(rspi, t); - else - ret = send_pio(rspi, mesg, t); - if (ret < 0) - goto error; - } - if (t->rx_buf) { - if (rspi_is_dma(rspi, t)) - ret = rspi_receive_dma(rspi, t); - else - ret = receive_pio(rspi, mesg, t); - if (ret < 0) - goto error; - } - mesg->actual_length += t->len; + rspi_receive_init(rspi); + + spcr = rspi_read8(rspi, RSPI_SPCR); + if (rx_buf) + spcr &= ~SPCR_TXMD; + else + spcr |= SPCR_TXMD; + rspi_write8(rspi, spcr, RSPI_SPCR); + + while (remain > 0) { + data = tx_buf ? *tx_buf++ : DUMMY_DATA; + ret = rspi_data_out(rspi, data); + if (ret < 0) + return ret; + if (rx_buf) { + ret = rspi_data_in(rspi); + if (ret < 0) + return ret; + *rx_buf++ = ret; } - rspi_negate_ssl(rspi); + remain--; + } + + /* Wait for the last transmission */ + rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + + return 0; +} + +static int rspi_transfer_one(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct rspi_data *rspi = spi_master_get_devdata(master); + int ret; + + if (!rspi_is_dma(rspi, xfer)) + return rspi_transfer_out_in(rspi, xfer); + + if (xfer->tx_buf) { + ret = rspi_send_dma(rspi, xfer); + if (ret < 0) + return ret; + } + if (xfer->rx_buf) + return rspi_receive_dma(rspi, xfer); + + return 0; +} + +static int rspi_rz_transfer_out_in(struct rspi_data *rspi, + struct spi_transfer *xfer) +{ + int remain = xfer->len, ret; + const u8 *tx_buf = xfer->tx_buf; + u8 *rx_buf = xfer->rx_buf; + u8 data; + + rspi_rz_receive_init(rspi); + + while (remain > 0) { + data = tx_buf ? *tx_buf++ : DUMMY_DATA; + ret = rspi_data_out_in(rspi, data); + if (ret < 0) + return ret; + if (rx_buf) + *rx_buf++ = ret; + remain--; + } + + /* Wait for the last transmission */ + rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + + return 0; +} + +static int rspi_rz_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct rspi_data *rspi = spi_master_get_devdata(master); + + return rspi_rz_transfer_out_in(rspi, xfer); +} + +static int qspi_transfer_out_in(struct rspi_data *rspi, + struct spi_transfer *xfer) +{ + int remain = xfer->len, ret; + const u8 *tx_buf = xfer->tx_buf; + u8 *rx_buf = xfer->rx_buf; + u8 data; - mesg->status = 0; - mesg->complete(mesg->context); + qspi_receive_init(rspi); + + while (remain > 0) { + data = tx_buf ? *tx_buf++ : DUMMY_DATA; + ret = rspi_data_out_in(rspi, data); + if (ret < 0) + return ret; + if (rx_buf) + *rx_buf++ = ret; + remain--; + } + + /* Wait for the last transmission */ + rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); + + return 0; +} + +static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) +{ + const u8 *buf = xfer->tx_buf; + unsigned int i; + int ret; + + for (i = 0; i < xfer->len; i++) { + ret = rspi_data_out(rspi, *buf++); + if (ret < 0) + return ret; } - return; + /* Wait for the last transmission */ + rspi_wait_for_interrupt(rspi, SPSR_SPTEF, SPCR_SPTIE); -error: - mesg->status = ret; - mesg->complete(mesg->context); + return 0; +} + +static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer) +{ + u8 *buf = xfer->rx_buf; + unsigned int i; + int ret; + + for (i = 0; i < xfer->len; i++) { + ret = rspi_data_in(rspi); + if (ret < 0) + return ret; + *buf++ = ret; + } + + return 0; +} + +static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct rspi_data *rspi = spi_master_get_devdata(master); + + if (spi->mode & SPI_LOOP) { + return qspi_transfer_out_in(rspi, xfer); + } else if (xfer->tx_buf && xfer->tx_nbits > SPI_NBITS_SINGLE) { + /* Quad or Dual SPI Write */ + return qspi_transfer_out(rspi, xfer); + } else if (xfer->rx_buf && xfer->rx_nbits > SPI_NBITS_SINGLE) { + /* Quad or Dual SPI Read */ + return qspi_transfer_in(rspi, xfer); + } else { + /* Single SPI Transfer */ + return qspi_transfer_out_in(rspi, xfer); + } } static int rspi_setup(struct spi_device *spi) @@ -810,32 +886,115 @@ static int rspi_setup(struct spi_device *spi) if (spi->mode & SPI_CPHA) rspi->spcmd |= SPCMD_CPHA; + /* CMOS output mode and MOSI signal from previous transfer */ + rspi->sppcr = 0; + if (spi->mode & SPI_LOOP) + rspi->sppcr |= SPPCR_SPLP; + set_config_register(rspi, 8); return 0; } -static int rspi_transfer(struct spi_device *spi, struct spi_message *mesg) +static u16 qspi_transfer_mode(const struct spi_transfer *xfer) { - struct rspi_data *rspi = spi_master_get_devdata(spi->master); - unsigned long flags; + if (xfer->tx_buf) + switch (xfer->tx_nbits) { + case SPI_NBITS_QUAD: + return SPCMD_SPIMOD_QUAD; + case SPI_NBITS_DUAL: + return SPCMD_SPIMOD_DUAL; + default: + return 0; + } + if (xfer->rx_buf) + switch (xfer->rx_nbits) { + case SPI_NBITS_QUAD: + return SPCMD_SPIMOD_QUAD | SPCMD_SPRW; + case SPI_NBITS_DUAL: + return SPCMD_SPIMOD_DUAL | SPCMD_SPRW; + default: + return 0; + } + + return 0; +} - mesg->actual_length = 0; - mesg->status = -EINPROGRESS; +static int qspi_setup_sequencer(struct rspi_data *rspi, + const struct spi_message *msg) +{ + const struct spi_transfer *xfer; + unsigned int i = 0, len = 0; + u16 current_mode = 0xffff, mode; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + mode = qspi_transfer_mode(xfer); + if (mode == current_mode) { + len += xfer->len; + continue; + } + + /* Transfer mode change */ + if (i) { + /* Set transfer data length of previous transfer */ + rspi_write32(rspi, len, QSPI_SPBMUL(i - 1)); + } - spin_lock_irqsave(&rspi->lock, flags); - list_add_tail(&mesg->queue, &rspi->queue); - schedule_work(&rspi->ws); - spin_unlock_irqrestore(&rspi->lock, flags); + if (i >= QSPI_NUM_SPCMD) { + dev_err(&msg->spi->dev, + "Too many different transfer modes"); + return -EINVAL; + } + + /* Program transfer mode for this transfer */ + rspi_write16(rspi, rspi->spcmd | mode, RSPI_SPCMD(i)); + current_mode = mode; + len = xfer->len; + i++; + } + if (i) { + /* Set final transfer data length and sequence length */ + rspi_write32(rspi, len, QSPI_SPBMUL(i - 1)); + rspi_write8(rspi, i - 1, RSPI_SPSCR); + } return 0; } -static void rspi_cleanup(struct spi_device *spi) +static int rspi_prepare_message(struct spi_master *master, + struct spi_message *msg) { + struct rspi_data *rspi = spi_master_get_devdata(master); + int ret; + + if (msg->spi->mode & + (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)) { + /* Setup sequencer for messages with multiple transfer modes */ + ret = qspi_setup_sequencer(rspi, msg); + if (ret < 0) + return ret; + } + + /* Enable SPI function in master mode */ + rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) | SPCR_SPE, RSPI_SPCR); + return 0; } -static irqreturn_t rspi_irq(int irq, void *_sr) +static int rspi_unprepare_message(struct spi_master *master, + struct spi_message *msg) +{ + struct rspi_data *rspi = spi_master_get_devdata(master); + + /* Disable SPI function */ + rspi_write8(rspi, rspi_read8(rspi, RSPI_SPCR) & ~SPCR_SPE, RSPI_SPCR); + + /* Reset sequencer for Single SPI Transfers */ + rspi_write16(rspi, rspi->spcmd, RSPI_SPCMD0); + rspi_write8(rspi, 0, RSPI_SPSCR); + return 0; +} + +static irqreturn_t rspi_irq_mux(int irq, void *_sr) { struct rspi_data *rspi = _sr; u8 spsr; @@ -857,6 +1016,36 @@ static irqreturn_t rspi_irq(int irq, void *_sr) return ret; } +static irqreturn_t rspi_irq_rx(int irq, void *_sr) +{ + struct rspi_data *rspi = _sr; + u8 spsr; + + rspi->spsr = spsr = rspi_read8(rspi, RSPI_SPSR); + if (spsr & SPSR_SPRF) { + rspi_disable_irq(rspi, SPCR_SPRIE); + wake_up(&rspi->wait); + return IRQ_HANDLED; + } + + return 0; +} + +static irqreturn_t rspi_irq_tx(int irq, void *_sr) +{ + struct rspi_data *rspi = _sr; + u8 spsr; + + rspi->spsr = spsr = rspi_read8(rspi, RSPI_SPSR); + if (spsr & SPSR_SPTEF) { + rspi_disable_irq(rspi, SPCR_SPTIE); + wake_up(&rspi->wait); + return IRQ_HANDLED; + } + + return 0; +} + static int rspi_request_dma(struct rspi_data *rspi, struct platform_device *pdev) { @@ -923,34 +1112,89 @@ static int rspi_remove(struct platform_device *pdev) struct rspi_data *rspi = platform_get_drvdata(pdev); rspi_release_dma(rspi); - clk_disable(rspi->clk); + pm_runtime_disable(&pdev->dev); return 0; } +static const struct spi_ops rspi_ops = { + .set_config_register = rspi_set_config_register, + .transfer_one = rspi_transfer_one, + .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, +}; + +static const struct spi_ops rspi_rz_ops = { + .set_config_register = rspi_rz_set_config_register, + .transfer_one = rspi_rz_transfer_one, + .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP, +}; + +static const struct spi_ops qspi_ops = { + .set_config_register = qspi_set_config_register, + .transfer_one = qspi_transfer_one, + .mode_bits = SPI_CPHA | SPI_CPOL | SPI_LOOP | + SPI_TX_DUAL | SPI_TX_QUAD | + SPI_RX_DUAL | SPI_RX_QUAD, +}; + +#ifdef CONFIG_OF +static const struct of_device_id rspi_of_match[] = { + /* RSPI on legacy SH */ + { .compatible = "renesas,rspi", .data = &rspi_ops }, + /* RSPI on RZ/A1H */ + { .compatible = "renesas,rspi-rz", .data = &rspi_rz_ops }, + /* QSPI on R-Car Gen2 */ + { .compatible = "renesas,qspi", .data = &qspi_ops }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, rspi_of_match); + +static int rspi_parse_dt(struct device *dev, struct spi_master *master) +{ + u32 num_cs; + int error; + + /* Parse DT properties */ + error = of_property_read_u32(dev->of_node, "num-cs", &num_cs); + if (error) { + dev_err(dev, "of_property_read_u32 num-cs failed %d\n", error); + return error; + } + + master->num_chipselect = num_cs; + return 0; +} +#else +#define rspi_of_match NULL +static inline int rspi_parse_dt(struct device *dev, struct spi_master *master) +{ + return -EINVAL; +} +#endif /* CONFIG_OF */ + +static int rspi_request_irq(struct device *dev, unsigned int irq, + irq_handler_t handler, const char *suffix, + void *dev_id) +{ + const char *base = dev_name(dev); + size_t len = strlen(base) + strlen(suffix) + 2; + char *name = devm_kzalloc(dev, len, GFP_KERNEL); + if (!name) + return -ENOMEM; + snprintf(name, len, "%s:%s", base, suffix); + return devm_request_irq(dev, irq, handler, 0, name, dev_id); +} + static int rspi_probe(struct platform_device *pdev) { struct resource *res; struct spi_master *master; struct rspi_data *rspi; - int ret, irq; - char clk_name[16]; - const struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev); + int ret; + const struct of_device_id *of_id; + const struct rspi_plat_data *rspi_pd; const struct spi_ops *ops; - const struct platform_device_id *id_entry = pdev->id_entry; - - ops = (struct spi_ops *)id_entry->driver_data; - /* ops parameter check */ - if (!ops->set_config_register) { - dev_err(&pdev->dev, "there is no set_config_register\n"); - return -ENODEV; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "platform_get_irq error\n"); - return -ENODEV; - } master = spi_alloc_master(&pdev->dev, sizeof(struct rspi_data)); if (master == NULL) { @@ -958,6 +1202,28 @@ static int rspi_probe(struct platform_device *pdev) return -ENOMEM; } + of_id = of_match_device(rspi_of_match, &pdev->dev); + if (of_id) { + ops = of_id->data; + ret = rspi_parse_dt(&pdev->dev, master); + if (ret) + goto error1; + } else { + ops = (struct spi_ops *)pdev->id_entry->driver_data; + rspi_pd = dev_get_platdata(&pdev->dev); + if (rspi_pd && rspi_pd->num_chipselect) + master->num_chipselect = rspi_pd->num_chipselect; + else + master->num_chipselect = 2; /* default */ + }; + + /* ops parameter check */ + if (!ops->set_config_register) { + dev_err(&pdev->dev, "there is no set_config_register\n"); + ret = -ENODEV; + goto error1; + } + rspi = spi_master_get_devdata(master); platform_set_drvdata(pdev, rspi); rspi->ops = ops; @@ -970,39 +1236,61 @@ static int rspi_probe(struct platform_device *pdev) goto error1; } - snprintf(clk_name, sizeof(clk_name), "%s%d", id_entry->name, pdev->id); - rspi->clk = devm_clk_get(&pdev->dev, clk_name); + rspi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(rspi->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); ret = PTR_ERR(rspi->clk); goto error1; } - clk_enable(rspi->clk); - INIT_LIST_HEAD(&rspi->queue); - spin_lock_init(&rspi->lock); - INIT_WORK(&rspi->ws, rspi_work); - init_waitqueue_head(&rspi->wait); + pm_runtime_enable(&pdev->dev); - if (rspi_pd && rspi_pd->num_chipselect) - master->num_chipselect = rspi_pd->num_chipselect; - else - master->num_chipselect = 2; /* default */ + init_waitqueue_head(&rspi->wait); master->bus_num = pdev->id; master->setup = rspi_setup; - master->transfer = rspi_transfer; - master->cleanup = rspi_cleanup; - master->mode_bits = SPI_CPHA | SPI_CPOL; + master->auto_runtime_pm = true; + master->transfer_one = ops->transfer_one; + master->prepare_message = rspi_prepare_message; + master->unprepare_message = rspi_unprepare_message; + master->mode_bits = ops->mode_bits; + master->dev.of_node = pdev->dev.of_node; + + ret = platform_get_irq_byname(pdev, "rx"); + if (ret < 0) { + ret = platform_get_irq_byname(pdev, "mux"); + if (ret < 0) + ret = platform_get_irq(pdev, 0); + if (ret >= 0) + rspi->rx_irq = rspi->tx_irq = ret; + } else { + rspi->rx_irq = ret; + ret = platform_get_irq_byname(pdev, "tx"); + if (ret >= 0) + rspi->tx_irq = ret; + } + if (ret < 0) { + dev_err(&pdev->dev, "platform_get_irq error\n"); + goto error2; + } - ret = devm_request_irq(&pdev->dev, irq, rspi_irq, 0, - dev_name(&pdev->dev), rspi); + if (rspi->rx_irq == rspi->tx_irq) { + /* Single multiplexed interrupt */ + ret = rspi_request_irq(&pdev->dev, rspi->rx_irq, rspi_irq_mux, + "mux", rspi); + } else { + /* Multi-interrupt mode, only SPRI and SPTI are used */ + ret = rspi_request_irq(&pdev->dev, rspi->rx_irq, rspi_irq_rx, + "rx", rspi); + if (!ret) + ret = rspi_request_irq(&pdev->dev, rspi->tx_irq, + rspi_irq_tx, "tx", rspi); + } if (ret < 0) { dev_err(&pdev->dev, "request_irq error\n"); goto error2; } - rspi->irq = irq; ret = rspi_request_dma(rspi, pdev); if (ret < 0) { dev_err(&pdev->dev, "rspi_request_dma failed.\n"); @@ -1022,27 +1310,16 @@ static int rspi_probe(struct platform_device *pdev) error3: rspi_release_dma(rspi); error2: - clk_disable(rspi->clk); + pm_runtime_disable(&pdev->dev); error1: spi_master_put(master); return ret; } -static struct spi_ops rspi_ops = { - .set_config_register = rspi_set_config_register, - .send_pio = rspi_send_pio, - .receive_pio = rspi_receive_pio, -}; - -static struct spi_ops qspi_ops = { - .set_config_register = qspi_set_config_register, - .send_pio = qspi_send_pio, - .receive_pio = qspi_receive_pio, -}; - static struct platform_device_id spi_driver_ids[] = { { "rspi", (kernel_ulong_t)&rspi_ops }, + { "rspi-rz", (kernel_ulong_t)&rspi_rz_ops }, { "qspi", (kernel_ulong_t)&qspi_ops }, {}, }; @@ -1056,6 +1333,7 @@ static struct platform_driver rspi_driver = { .driver = { .name = "renesas_spi", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rspi_of_match), }, }; module_platform_driver(rspi_driver); diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index 746424aa5353..bed23384dfab 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -9,7 +9,6 @@ * */ -#include <linux/init.h> #include <linux/spinlock.h> #include <linux/workqueue.h> #include <linux/interrupt.h> @@ -123,25 +122,15 @@ static int s3c24xx_spi_update_state(struct spi_device *spi, { struct s3c24xx_spi *hw = to_hw(spi); struct s3c24xx_spi_devstate *cs = spi->controller_state; - unsigned int bpw; unsigned int hz; unsigned int div; unsigned long clk; - bpw = t ? t->bits_per_word : spi->bits_per_word; hz = t ? t->speed_hz : spi->max_speed_hz; - if (!bpw) - bpw = 8; - if (!hz) hz = spi->max_speed_hz; - if (bpw != 8) { - dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); - return -EINVAL; - } - if (spi->mode != cs->mode) { u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK; @@ -544,6 +533,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) master->num_chipselect = hw->pdata->num_cs; master->bus_num = pdata->bus_num; + master->bits_per_word_mask = SPI_BPW_MASK(8); /* setup the state for the bitbang driver */ @@ -643,6 +633,11 @@ static int s3c24xx_spi_remove(struct platform_device *dev) static int s3c24xx_spi_suspend(struct device *dev) { struct s3c24xx_spi *hw = dev_get_drvdata(dev); + int ret; + + ret = spi_master_suspend(hw->master); + if (ret) + return ret; if (hw->pdata && hw->pdata->gpio_setup) hw->pdata->gpio_setup(hw->pdata, 0); @@ -656,7 +651,7 @@ static int s3c24xx_spi_resume(struct device *dev) struct s3c24xx_spi *hw = dev_get_drvdata(dev); s3c24xx_spi_initialsetup(hw); - return 0; + return spi_master_resume(hw->master); } static const struct dev_pm_ops s3c24xx_spi_pmops = { diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index ae907dde1371..f19cd97855e8 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -34,10 +34,6 @@ #include <linux/platform_data/spi-s3c64xx.h> -#ifdef CONFIG_S3C_DMA -#include <mach/dma.h> -#endif - #define MAX_SPI_PORTS 3 #define S3C64XX_SPI_QUIRK_POLL (1 << 0) @@ -200,9 +196,6 @@ struct s3c64xx_spi_driver_data { unsigned cur_speed; struct s3c64xx_spi_dma_data rx_dma; struct s3c64xx_spi_dma_data tx_dma; -#ifdef CONFIG_S3C_DMA - struct samsung_dma_ops *ops; -#endif struct s3c64xx_spi_port_config *port_conf; unsigned int port_id; bool cs_gpio; @@ -284,104 +277,8 @@ static void s3c64xx_spi_dmacb(void *data) spin_unlock_irqrestore(&sdd->lock, flags); } -#ifdef CONFIG_S3C_DMA -/* FIXME: remove this section once arch/arm/mach-s3c64xx uses dmaengine */ - -static struct s3c2410_dma_client s3c64xx_spi_dma_client = { - .name = "samsung-spi-dma", -}; - -static void prepare_dma(struct s3c64xx_spi_dma_data *dma, - unsigned len, dma_addr_t buf) -{ - struct s3c64xx_spi_driver_data *sdd; - struct samsung_dma_prep info; - struct samsung_dma_config config; - - if (dma->direction == DMA_DEV_TO_MEM) { - sdd = container_of((void *)dma, - struct s3c64xx_spi_driver_data, rx_dma); - config.direction = sdd->rx_dma.direction; - config.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA; - config.width = sdd->cur_bpw / 8; - sdd->ops->config((enum dma_ch)sdd->rx_dma.ch, &config); - } else { - sdd = container_of((void *)dma, - struct s3c64xx_spi_driver_data, tx_dma); - config.direction = sdd->tx_dma.direction; - config.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA; - config.width = sdd->cur_bpw / 8; - sdd->ops->config((enum dma_ch)sdd->tx_dma.ch, &config); - } - - info.cap = DMA_SLAVE; - info.len = len; - info.fp = s3c64xx_spi_dmacb; - info.fp_param = dma; - info.direction = dma->direction; - info.buf = buf; - - sdd->ops->prepare((enum dma_ch)dma->ch, &info); - sdd->ops->trigger((enum dma_ch)dma->ch); -} - -static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) -{ - struct samsung_dma_req req; - struct device *dev = &sdd->pdev->dev; - - sdd->ops = samsung_dma_get_ops(); - - req.cap = DMA_SLAVE; - req.client = &s3c64xx_spi_dma_client; - - sdd->rx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request( - sdd->rx_dma.dmach, &req, dev, "rx"); - sdd->tx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request( - sdd->tx_dma.dmach, &req, dev, "tx"); - - return 1; -} - -static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) -{ - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); - - /* - * If DMA resource was not available during - * probe, no need to continue with dma requests - * else Acquire DMA channels - */ - while (!is_polling(sdd) && !acquire_dma(sdd)) - usleep_range(10000, 11000); - - return 0; -} - -static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) -{ - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); - - /* Free DMA channels */ - if (!is_polling(sdd)) { - sdd->ops->release((enum dma_ch)sdd->rx_dma.ch, - &s3c64xx_spi_dma_client); - sdd->ops->release((enum dma_ch)sdd->tx_dma.ch, - &s3c64xx_spi_dma_client); - } - - return 0; -} - -static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd, - struct s3c64xx_spi_dma_data *dma) -{ - sdd->ops->stop((enum dma_ch)dma->ch); -} -#else - static void prepare_dma(struct s3c64xx_spi_dma_data *dma, - unsigned len, dma_addr_t buf) + struct sg_table *sgt) { struct s3c64xx_spi_driver_data *sdd; struct dma_slave_config config; @@ -407,8 +304,8 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, dmaengine_slave_config(dma->ch, &config); } - desc = dmaengine_prep_slave_single(dma->ch, buf, len, - dma->direction, DMA_PREP_INTERRUPT); + desc = dmaengine_prep_slave_sg(dma->ch, sgt->sgl, sgt->nents, + dma->direction, DMA_PREP_INTERRUPT); desc->callback = s3c64xx_spi_dmacb; desc->callback_param = dma; @@ -437,6 +334,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) ret = -EBUSY; goto out; } + spi->dma_rx = sdd->rx_dma.ch; sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, (void *)sdd->tx_dma.dmach, dev, "tx"); @@ -445,6 +343,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) ret = -EBUSY; goto out_rx; } + spi->dma_tx = sdd->tx_dma.ch; } ret = pm_runtime_get_sync(&sdd->pdev->dev); @@ -477,12 +376,14 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) return 0; } -static void s3c64xx_spi_dma_stop(struct s3c64xx_spi_driver_data *sdd, - struct s3c64xx_spi_dma_data *dma) +static bool s3c64xx_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) { - dmaengine_terminate_all(dma->ch); + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); + + return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1; } -#endif static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi, @@ -515,7 +416,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, chcfg |= S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) { modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; - prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma); + prepare_dma(&sdd->tx_dma, &xfer->tx_sg); } else { switch (sdd->cur_bpw) { case 32: @@ -547,7 +448,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | S3C64XX_SPI_PACKET_CNT_EN, regs + S3C64XX_SPI_PACKET_CNT); - prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma); + prepare_dma(&sdd->rx_dma, &xfer->rx_sg); } } @@ -555,23 +456,6 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, writel(chcfg, regs + S3C64XX_SPI_CH_CFG); } -static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd, - struct spi_device *spi) -{ - if (sdd->tgl_spi != NULL) { /* If last device toggled after mssg */ - if (sdd->tgl_spi != spi) { /* if last mssg on diff device */ - /* Deselect the last toggled device */ - if (spi->cs_gpio >= 0) - gpio_set_value(spi->cs_gpio, - spi->mode & SPI_CS_HIGH ? 0 : 1); - } - sdd->tgl_spi = NULL; - } - - if (spi->cs_gpio >= 0) - gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 1 : 0); -} - static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, int timeout_ms) { @@ -593,112 +477,111 @@ static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, return RX_FIFO_LVL(status, sdd); } -static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, - struct spi_transfer *xfer, int dma_mode) +static int wait_for_dma(struct s3c64xx_spi_driver_data *sdd, + struct spi_transfer *xfer) { void __iomem *regs = sdd->regs; unsigned long val; + u32 status; int ms; /* millisecs to xfer 'len' bytes @ 'cur_speed' */ ms = xfer->len * 8 * 1000 / sdd->cur_speed; ms += 10; /* some tolerance */ - if (dma_mode) { - val = msecs_to_jiffies(ms) + 10; - val = wait_for_completion_timeout(&sdd->xfer_completion, val); - } else { - u32 status; - val = msecs_to_loops(ms); - do { + val = msecs_to_jiffies(ms) + 10; + val = wait_for_completion_timeout(&sdd->xfer_completion, val); + + /* + * If the previous xfer was completed within timeout, then + * proceed further else return -EIO. + * DmaTx returns after simply writing data in the FIFO, + * w/o waiting for real transmission on the bus to finish. + * DmaRx returns only after Dma read data from FIFO which + * needs bus transmission to finish, so we don't worry if + * Xfer involved Rx(with or without Tx). + */ + if (val && !xfer->rx_buf) { + val = msecs_to_loops(10); + status = readl(regs + S3C64XX_SPI_STATUS); + while ((TX_FIFO_LVL(status, sdd) + || !S3C64XX_SPI_ST_TX_DONE(status, sdd)) + && --val) { + cpu_relax(); status = readl(regs + S3C64XX_SPI_STATUS); - } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val); + } + } - if (dma_mode) { - u32 status; - - /* - * If the previous xfer was completed within timeout, then - * proceed further else return -EIO. - * DmaTx returns after simply writing data in the FIFO, - * w/o waiting for real transmission on the bus to finish. - * DmaRx returns only after Dma read data from FIFO which - * needs bus transmission to finish, so we don't worry if - * Xfer involved Rx(with or without Tx). - */ - if (val && !xfer->rx_buf) { - val = msecs_to_loops(10); - status = readl(regs + S3C64XX_SPI_STATUS); - while ((TX_FIFO_LVL(status, sdd) - || !S3C64XX_SPI_ST_TX_DONE(status, sdd)) - && --val) { - cpu_relax(); - status = readl(regs + S3C64XX_SPI_STATUS); - } + /* If timed out while checking rx/tx status return error */ + if (!val) + return -EIO; - } + return 0; +} - /* If timed out while checking rx/tx status return error */ - if (!val) - return -EIO; - } else { - int loops; - u32 cpy_len; - u8 *buf; - - /* If it was only Tx */ - if (!xfer->rx_buf) { - sdd->state &= ~TXBUSY; - return 0; - } +static int wait_for_pio(struct s3c64xx_spi_driver_data *sdd, + struct spi_transfer *xfer) +{ + void __iomem *regs = sdd->regs; + unsigned long val; + u32 status; + int loops; + u32 cpy_len; + u8 *buf; + int ms; - /* - * If the receive length is bigger than the controller fifo - * size, calculate the loops and read the fifo as many times. - * loops = length / max fifo size (calculated by using the - * fifo mask). - * For any size less than the fifo size the below code is - * executed atleast once. - */ - loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1); - buf = xfer->rx_buf; - do { - /* wait for data to be received in the fifo */ - cpy_len = s3c64xx_spi_wait_for_timeout(sdd, - (loops ? ms : 0)); + /* millisecs to xfer 'len' bytes @ 'cur_speed' */ + ms = xfer->len * 8 * 1000 / sdd->cur_speed; + ms += 10; /* some tolerance */ - switch (sdd->cur_bpw) { - case 32: - ioread32_rep(regs + S3C64XX_SPI_RX_DATA, - buf, cpy_len / 4); - break; - case 16: - ioread16_rep(regs + S3C64XX_SPI_RX_DATA, - buf, cpy_len / 2); - break; - default: - ioread8_rep(regs + S3C64XX_SPI_RX_DATA, - buf, cpy_len); - break; - } + val = msecs_to_loops(ms); + do { + status = readl(regs + S3C64XX_SPI_STATUS); + } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val); - buf = buf + cpy_len; - } while (loops--); - sdd->state &= ~RXBUSY; + + /* If it was only Tx */ + if (!xfer->rx_buf) { + sdd->state &= ~TXBUSY; + return 0; } - return 0; -} + /* + * If the receive length is bigger than the controller fifo + * size, calculate the loops and read the fifo as many times. + * loops = length / max fifo size (calculated by using the + * fifo mask). + * For any size less than the fifo size the below code is + * executed atleast once. + */ + loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1); + buf = xfer->rx_buf; + do { + /* wait for data to be received in the fifo */ + cpy_len = s3c64xx_spi_wait_for_timeout(sdd, + (loops ? ms : 0)); + + switch (sdd->cur_bpw) { + case 32: + ioread32_rep(regs + S3C64XX_SPI_RX_DATA, + buf, cpy_len / 4); + break; + case 16: + ioread16_rep(regs + S3C64XX_SPI_RX_DATA, + buf, cpy_len / 2); + break; + default: + ioread8_rep(regs + S3C64XX_SPI_RX_DATA, + buf, cpy_len); + break; + } -static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, - struct spi_device *spi) -{ - if (sdd->tgl_spi == spi) - sdd->tgl_spi = NULL; + buf = buf + cpy_len; + } while (loops--); + sdd->state &= ~RXBUSY; - if (spi->cs_gpio >= 0) - gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); + return 0; } static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) @@ -774,81 +657,6 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) #define XFER_DMAADDR_INVALID DMA_BIT_MASK(32) -static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, - struct spi_message *msg) -{ - struct device *dev = &sdd->pdev->dev; - struct spi_transfer *xfer; - - if (is_polling(sdd) || msg->is_dma_mapped) - return 0; - - /* First mark all xfer unmapped */ - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - xfer->rx_dma = XFER_DMAADDR_INVALID; - xfer->tx_dma = XFER_DMAADDR_INVALID; - } - - /* Map until end or first fail */ - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - - if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1)) - continue; - - if (xfer->tx_buf != NULL) { - xfer->tx_dma = dma_map_single(dev, - (void *)xfer->tx_buf, xfer->len, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, xfer->tx_dma)) { - dev_err(dev, "dma_map_single Tx failed\n"); - xfer->tx_dma = XFER_DMAADDR_INVALID; - return -ENOMEM; - } - } - - if (xfer->rx_buf != NULL) { - xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, - xfer->len, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, xfer->rx_dma)) { - dev_err(dev, "dma_map_single Rx failed\n"); - dma_unmap_single(dev, xfer->tx_dma, - xfer->len, DMA_TO_DEVICE); - xfer->tx_dma = XFER_DMAADDR_INVALID; - xfer->rx_dma = XFER_DMAADDR_INVALID; - return -ENOMEM; - } - } - } - - return 0; -} - -static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, - struct spi_message *msg) -{ - struct device *dev = &sdd->pdev->dev; - struct spi_transfer *xfer; - - if (is_polling(sdd) || msg->is_dma_mapped) - return; - - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - - if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1)) - continue; - - if (xfer->rx_buf != NULL - && xfer->rx_dma != XFER_DMAADDR_INVALID) - dma_unmap_single(dev, xfer->rx_dma, - xfer->len, DMA_FROM_DEVICE); - - if (xfer->tx_buf != NULL - && xfer->tx_dma != XFER_DMAADDR_INVALID) - dma_unmap_single(dev, xfer->tx_dma, - xfer->len, DMA_TO_DEVICE); - } -} - static int s3c64xx_spi_prepare_message(struct spi_master *master, struct spi_message *msg) { @@ -866,13 +674,6 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master, s3c64xx_spi_config(sdd); } - /* Map all the transfers if needed */ - if (s3c64xx_spi_map_mssg(sdd, msg)) { - dev_err(&spi->dev, - "Xfer: Unable to map message buffers!\n"); - return -ENOMEM; - } - /* Configure feedback delay */ writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); @@ -896,13 +697,6 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, bpw = xfer->bits_per_word; speed = xfer->speed_hz ? : spi->max_speed_hz; - if (xfer->len % (bpw / 8)) { - dev_err(&spi->dev, - "Xfer length(%u) not a multiple of word size(%u)\n", - xfer->len, bpw / 8); - return -EIO; - } - if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { sdd->cur_bpw = bpw; sdd->cur_speed = speed; @@ -929,7 +723,10 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, spin_unlock_irqrestore(&sdd->lock, flags); - status = wait_for_xfer(sdd, xfer, use_dma); + if (use_dma) + status = wait_for_dma(sdd, xfer); + else + status = wait_for_pio(sdd, xfer); if (status) { dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n", @@ -941,10 +738,10 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, if (use_dma) { if (xfer->tx_buf != NULL && (sdd->state & TXBUSY)) - s3c64xx_spi_dma_stop(sdd, &sdd->tx_dma); + dmaengine_terminate_all(sdd->tx_dma.ch); if (xfer->rx_buf != NULL && (sdd->state & RXBUSY)) - s3c64xx_spi_dma_stop(sdd, &sdd->rx_dma); + dmaengine_terminate_all(sdd->rx_dma.ch); } } else { flush_fifo(sdd); @@ -953,16 +750,6 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, return status; } -static int s3c64xx_spi_unprepare_message(struct spi_master *master, - struct spi_message *msg) -{ - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); - - s3c64xx_spi_unmap_mssg(sdd, msg); - - return 0; -} - static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( struct spi_device *spi) { @@ -1092,14 +879,12 @@ static int s3c64xx_spi_setup(struct spi_device *spi) pm_runtime_put(&sdd->pdev->dev); writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); - disable_cs(sdd, spi); return 0; setup_exit: pm_runtime_put(&sdd->pdev->dev); /* setup() returns with device de-selected */ writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); - disable_cs(sdd, spi); gpio_free(cs->line); spi_set_ctldata(spi, NULL); @@ -1338,7 +1123,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer; master->prepare_message = s3c64xx_spi_prepare_message; master->transfer_one = s3c64xx_spi_transfer_one; - master->unprepare_message = s3c64xx_spi_unprepare_message; master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer; master->num_chipselect = sci->num_cs; master->dma_alignment = 8; @@ -1347,6 +1131,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->auto_runtime_pm = true; + if (!is_polling(sdd)) + master->can_dma = s3c64xx_spi_can_dma; sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res); if (IS_ERR(sdd->regs)) { diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index 121c2e1dea36..237f2e7a7179 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -183,17 +183,9 @@ static int sc18is602_setup_transfer(struct sc18is602 *hw, u32 hz, u8 mode) static int sc18is602_check_transfer(struct spi_device *spi, struct spi_transfer *t, int tlen) { - uint32_t hz; - if (t && t->len + tlen > SC18IS602_BUFSIZ) return -EINVAL; - hz = spi->max_speed_hz; - if (t && t->speed_hz) - hz = t->speed_hz; - if (hz == 0) - return -EINVAL; - return 0; } @@ -205,22 +197,15 @@ static int sc18is602_transfer_one(struct spi_master *master, struct spi_transfer *t; int status = 0; - /* SC18IS602 does not support CS2 */ - if (hw->id == sc18is602 && spi->chip_select == 2) { - status = -ENXIO; - goto error; - } - hw->tlen = 0; list_for_each_entry(t, &m->transfers, transfer_list) { - u32 hz = t->speed_hz ? : spi->max_speed_hz; bool do_transfer; status = sc18is602_check_transfer(spi, t, hw->tlen); if (status < 0) break; - status = sc18is602_setup_transfer(hw, hz, spi->mode); + status = sc18is602_setup_transfer(hw, t->speed_hz, spi->mode); if (status < 0) break; @@ -238,7 +223,6 @@ static int sc18is602_transfer_one(struct spi_master *master, if (t->delay_usecs) udelay(t->delay_usecs); } -error: m->status = status; spi_finalize_current_message(master); @@ -247,10 +231,13 @@ error: static int sc18is602_setup(struct spi_device *spi) { - if (spi->mode & ~(SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST)) - return -EINVAL; + struct sc18is602 *hw = spi_master_get_devdata(spi->master); - return sc18is602_check_transfer(spi, NULL, 0); + /* SC18IS602 does not support CS2 */ + if (hw->id == sc18is602 && spi->chip_select == 2) + return -ENXIO; + + return 0; } static int sc18is602_probe(struct i2c_client *client, @@ -309,6 +296,8 @@ static int sc18is602_probe(struct i2c_client *client, master->setup = sc18is602_setup; master->transfer_one_message = sc18is602_transfer_one; master->dev.of_node = np; + master->min_speed_hz = hw->freq / 128; + master->max_speed_hz = hw->freq / 4; error = devm_spi_register_master(dev, master); if (error) diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 82d2f922ffa0..9009456bdf4d 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -46,8 +46,6 @@ /* SPSR */ #define RXFL (1 << 2) -#define hspi2info(h) (h->dev->platform_data) - struct hspi_priv { void __iomem *addr; struct spi_master *master; @@ -113,14 +111,9 @@ static void hspi_hw_setup(struct hspi_priv *hspi, { struct spi_device *spi = msg->spi; struct device *dev = hspi->dev; - u32 target_rate; u32 spcr, idiv_clk; u32 rate, best_rate, min, tmp; - target_rate = t ? t->speed_hz : 0; - if (!target_rate) - target_rate = spi->max_speed_hz; - /* * find best IDIV/CLKCx settings */ @@ -140,7 +133,7 @@ static void hspi_hw_setup(struct hspi_priv *hspi, rate /= (((idiv_clk & 0x1F) + 1) * 2); /* save best settings */ - tmp = abs(target_rate - rate); + tmp = abs(t->speed_hz - rate); if (tmp < min) { min = tmp; spcr = idiv_clk; @@ -153,7 +146,7 @@ static void hspi_hw_setup(struct hspi_priv *hspi, if (spi->mode & SPI_CPOL) spcr |= 1 << 6; - dev_dbg(dev, "speed %d/%d\n", target_rate, best_rate); + dev_dbg(dev, "speed %d/%d\n", t->speed_hz, best_rate); hspi_write(hspi, SPCR, spcr); hspi_write(hspi, SPSR, 0x0); @@ -230,29 +223,6 @@ static int hspi_transfer_one_message(struct spi_master *master, return ret; } -static int hspi_setup(struct spi_device *spi) -{ - struct hspi_priv *hspi = spi_master_get_devdata(spi->master); - struct device *dev = hspi->dev; - - if (8 != spi->bits_per_word) { - dev_err(dev, "bits_per_word should be 8\n"); - return -EIO; - } - - dev_dbg(dev, "%s setup\n", spi->modalias); - - return 0; -} - -static void hspi_cleanup(struct spi_device *spi) -{ - struct hspi_priv *hspi = spi_master_get_devdata(spi->master); - struct device *dev = hspi->dev; - - dev_dbg(dev, "%s cleanup\n", spi->modalias); -} - static int hspi_probe(struct platform_device *pdev) { struct resource *res; @@ -298,22 +268,23 @@ static int hspi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - master->num_chipselect = 1; master->bus_num = pdev->id; - master->setup = hspi_setup; - master->cleanup = hspi_cleanup; master->mode_bits = SPI_CPOL | SPI_CPHA; master->dev.of_node = pdev->dev.of_node; master->auto_runtime_pm = true; master->transfer_one_message = hspi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(&pdev->dev, "spi_register_master error.\n"); - goto error1; + goto error2; } return 0; + error2: + pm_runtime_disable(&pdev->dev); error1: clk_put(clk); error0: diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 81cc02f5f9b0..e850d03e7190 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -15,59 +15,108 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/spi/sh_msiof.h> #include <linux/spi/spi.h> -#include <linux/spi/spi_bitbang.h> #include <asm/unaligned.h> + +struct sh_msiof_chipdata { + u16 tx_fifo_size; + u16 rx_fifo_size; + u16 master_flags; +}; + struct sh_msiof_spi_priv { - struct spi_bitbang bitbang; /* must be first for spi_bitbang.c */ void __iomem *mapbase; struct clk *clk; struct platform_device *pdev; + const struct sh_msiof_chipdata *chipdata; struct sh_msiof_spi_info *info; struct completion done; - unsigned long flags; int tx_fifo_size; int rx_fifo_size; }; -#define TMDR1 0x00 -#define TMDR2 0x04 -#define TMDR3 0x08 -#define RMDR1 0x10 -#define RMDR2 0x14 -#define RMDR3 0x18 -#define TSCR 0x20 -#define RSCR 0x22 -#define CTR 0x28 -#define FCTR 0x30 -#define STR 0x40 -#define IER 0x44 -#define TDR1 0x48 -#define TDR2 0x4c -#define TFDR 0x50 -#define RDR1 0x58 -#define RDR2 0x5c -#define RFDR 0x60 - -#define CTR_TSCKE (1 << 15) -#define CTR_TFSE (1 << 14) -#define CTR_TXE (1 << 9) -#define CTR_RXE (1 << 8) - -#define STR_TEOF (1 << 23) -#define STR_REOF (1 << 7) +#define TMDR1 0x00 /* Transmit Mode Register 1 */ +#define TMDR2 0x04 /* Transmit Mode Register 2 */ +#define TMDR3 0x08 /* Transmit Mode Register 3 */ +#define RMDR1 0x10 /* Receive Mode Register 1 */ +#define RMDR2 0x14 /* Receive Mode Register 2 */ +#define RMDR3 0x18 /* Receive Mode Register 3 */ +#define TSCR 0x20 /* Transmit Clock Select Register */ +#define RSCR 0x22 /* Receive Clock Select Register (SH, A1, APE6) */ +#define CTR 0x28 /* Control Register */ +#define FCTR 0x30 /* FIFO Control Register */ +#define STR 0x40 /* Status Register */ +#define IER 0x44 /* Interrupt Enable Register */ +#define TDR1 0x48 /* Transmit Control Data Register 1 (SH, A1) */ +#define TDR2 0x4c /* Transmit Control Data Register 2 (SH, A1) */ +#define TFDR 0x50 /* Transmit FIFO Data Register */ +#define RDR1 0x58 /* Receive Control Data Register 1 (SH, A1) */ +#define RDR2 0x5c /* Receive Control Data Register 2 (SH, A1) */ +#define RFDR 0x60 /* Receive FIFO Data Register */ + +/* TMDR1 and RMDR1 */ +#define MDR1_TRMD 0x80000000 /* Transfer Mode (1 = Master mode) */ +#define MDR1_SYNCMD_MASK 0x30000000 /* SYNC Mode */ +#define MDR1_SYNCMD_SPI 0x20000000 /* Level mode/SPI */ +#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_FLD_SHIFT 2 +#define MDR1_XXSTP 0x00000001 /* Transmission/Reception Stop on FIFO */ +/* TMDR1 */ +#define TMDR1_PCON 0x40000000 /* Transfer Signal Connection */ + +/* TMDR2 and RMDR2 */ +#define MDR2_BITLEN1(i) (((i) - 1) << 24) /* Data Size (8-32 bits) */ +#define MDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */ +#define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */ + +/* TSCR and RSCR */ +#define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */ +#define SCR_BRPS(i) (((i) - 1) << 8) +#define SCR_BRDV_MASK 0x0007 /* Baud Rate Generator's Division Ratio */ +#define SCR_BRDV_DIV_2 0x0000 +#define SCR_BRDV_DIV_4 0x0001 +#define SCR_BRDV_DIV_8 0x0002 +#define SCR_BRDV_DIV_16 0x0003 +#define SCR_BRDV_DIV_32 0x0004 +#define SCR_BRDV_DIV_1 0x0007 + +/* CTR */ +#define CTR_TSCKIZ_MASK 0xc0000000 /* Transmit Clock I/O Polarity Select */ +#define CTR_TSCKIZ_SCK 0x80000000 /* Disable SCK when TX disabled */ +#define CTR_TSCKIZ_POL_SHIFT 30 /* Transmit Clock Polarity */ +#define CTR_RSCKIZ_MASK 0x30000000 /* Receive Clock Polarity Select */ +#define CTR_RSCKIZ_SCK 0x20000000 /* Must match CTR_TSCKIZ_SCK */ +#define CTR_RSCKIZ_POL_SHIFT 28 /* Receive Clock Polarity */ +#define CTR_TEDG_SHIFT 27 /* Transmit Timing (1 = falling edge) */ +#define CTR_REDG_SHIFT 26 /* Receive Timing (1 = falling edge) */ +#define CTR_TXDIZ_MASK 0x00c00000 /* Pin Output When TX is Disabled */ +#define CTR_TXDIZ_LOW 0x00000000 /* 0 */ +#define CTR_TXDIZ_HIGH 0x00400000 /* 1 */ +#define CTR_TXDIZ_HIZ 0x00800000 /* High-impedance */ +#define CTR_TSCKE 0x00008000 /* Transmit Serial Clock Output Enable */ +#define CTR_TFSE 0x00004000 /* Transmit Frame Sync Signal Output Enable */ +#define CTR_TXE 0x00000200 /* Transmit Enable */ +#define CTR_RXE 0x00000100 /* Receive Enable */ + +/* STR and IER */ +#define STR_TEOF 0x00800000 /* Frame Transmission End */ +#define STR_REOF 0x00000080 /* Frame Reception End */ + static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs) { @@ -131,22 +180,21 @@ static struct { unsigned short div; unsigned short scr; } const sh_msiof_spi_clk_table[] = { - { 1, 0x0007 }, - { 2, 0x0000 }, - { 4, 0x0001 }, - { 8, 0x0002 }, - { 16, 0x0003 }, - { 32, 0x0004 }, - { 64, 0x1f00 }, - { 128, 0x1f01 }, - { 256, 0x1f02 }, - { 512, 0x1f03 }, - { 1024, 0x1f04 }, + { 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 }, }; static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, - unsigned long parent_rate, - unsigned long spi_hz) + unsigned long parent_rate, u32 spi_hz) { unsigned long div = 1024; size_t k; @@ -164,7 +212,8 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_clk_table) - 1); sh_msiof_write(p, TSCR, sh_msiof_spi_clk_table[k].scr); - sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr); + if (!(p->chipdata->master_flags & SPI_MASTER_MUST_TX)) + sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr); } static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, @@ -183,21 +232,25 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, */ sh_msiof_write(p, FCTR, 0); - tmp = 0; - tmp |= !cs_high << 25; - tmp |= lsb_first << 24; - sh_msiof_write(p, TMDR1, 0xe0000005 | tmp); - sh_msiof_write(p, RMDR1, 0x20000005 | tmp); + tmp = MDR1_SYNCMD_SPI | 1 << MDR1_FLD_SHIFT | MDR1_XXSTP; + tmp |= !cs_high << MDR1_SYNCAC_SHIFT; + tmp |= lsb_first << MDR1_BITLSB_SHIFT; + 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 */ + tmp &= ~0x0000ffff; + } + sh_msiof_write(p, RMDR1, tmp); - tmp = 0xa0000000; - tmp |= cpol << 30; /* TSCKIZ */ - tmp |= cpol << 28; /* RSCKIZ */ + tmp = 0; + tmp |= CTR_TSCKIZ_SCK | cpol << CTR_TSCKIZ_POL_SHIFT; + tmp |= CTR_RSCKIZ_SCK | cpol << CTR_RSCKIZ_POL_SHIFT; edge = cpol ^ !cpha; - tmp |= edge << 27; /* TEDG */ - tmp |= edge << 26; /* REDG */ - tmp |= (tx_hi_z ? 2 : 0) << 22; /* TXDIZ */ + tmp |= edge << CTR_TEDG_SHIFT; + tmp |= edge << CTR_REDG_SHIFT; + tmp |= tx_hi_z ? CTR_TXDIZ_HIZ : CTR_TXDIZ_LOW; sh_msiof_write(p, CTR, tmp); } @@ -205,12 +258,12 @@ static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p, const void *tx_buf, void *rx_buf, u32 bits, u32 words) { - u32 dr2 = ((bits - 1) << 24) | ((words - 1) << 16); + u32 dr2 = MDR2_BITLEN1(bits) | MDR2_WDLEN1(words); - if (tx_buf) + if (tx_buf || (p->chipdata->master_flags & SPI_MASTER_MUST_TX)) sh_msiof_write(p, TMDR2, dr2); else - sh_msiof_write(p, TMDR2, dr2 | 1); + sh_msiof_write(p, TMDR2, dr2 | MDR2_GRPMASK1); if (rx_buf) sh_msiof_write(p, RMDR2, dr2); @@ -363,77 +416,45 @@ static void sh_msiof_spi_read_fifo_s32u(struct sh_msiof_spi_priv *p, put_unaligned(swab32(sh_msiof_read(p, RFDR) >> fs), &buf_32[k]); } -static int sh_msiof_spi_bits(struct spi_device *spi, struct spi_transfer *t) +static int sh_msiof_spi_setup(struct spi_device *spi) { - int bits; - - bits = t ? t->bits_per_word : 0; - if (!bits) - bits = spi->bits_per_word; - return bits; -} - -static unsigned long sh_msiof_spi_hz(struct spi_device *spi, - struct spi_transfer *t) -{ - unsigned long hz; - - hz = t ? t->speed_hz : 0; - if (!hz) - hz = spi->max_speed_hz; - return hz; -} + struct device_node *np = spi->master->dev.of_node; + struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master); -static int sh_msiof_spi_setup_transfer(struct spi_device *spi, - struct spi_transfer *t) -{ - int bits; + if (!np) { + /* + * Use spi->controller_data for CS (same strategy as spi_gpio), + * if any. otherwise let HW control CS + */ + spi->cs_gpio = (uintptr_t)spi->controller_data; + } - /* noting to check hz values against since parent clock is disabled */ + /* Configure pins before deasserting CS */ + sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL), + !!(spi->mode & SPI_CPHA), + !!(spi->mode & SPI_3WIRE), + !!(spi->mode & SPI_LSB_FIRST), + !!(spi->mode & SPI_CS_HIGH)); - bits = sh_msiof_spi_bits(spi, t); - if (bits < 8) - return -EINVAL; - if (bits > 32) - return -EINVAL; + if (spi->cs_gpio >= 0) + gpio_set_value(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); - return spi_bitbang_setup_transfer(spi, t); + return 0; } -static void sh_msiof_spi_chipselect(struct spi_device *spi, int is_on) +static int sh_msiof_prepare_message(struct spi_master *master, + struct spi_message *msg) { - struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master); - int value; - - /* chip select is active low unless SPI_CS_HIGH is set */ - if (spi->mode & SPI_CS_HIGH) - value = (is_on == BITBANG_CS_ACTIVE) ? 1 : 0; - else - value = (is_on == BITBANG_CS_ACTIVE) ? 0 : 1; - - if (is_on == BITBANG_CS_ACTIVE) { - if (!test_and_set_bit(0, &p->flags)) { - pm_runtime_get_sync(&p->pdev->dev); - clk_enable(p->clk); - } - - /* Configure pins before asserting CS */ - sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL), - !!(spi->mode & SPI_CPHA), - !!(spi->mode & SPI_3WIRE), - !!(spi->mode & SPI_LSB_FIRST), - !!(spi->mode & SPI_CS_HIGH)); - } + struct sh_msiof_spi_priv *p = spi_master_get_devdata(master); + const struct spi_device *spi = msg->spi; - /* use spi->controller data for CS (same strategy as spi_gpio) */ - gpio_set_value((uintptr_t)spi->controller_data, value); - - if (is_on == BITBANG_CS_INACTIVE) { - if (test_and_clear_bit(0, &p->flags)) { - clk_disable(p->clk); - pm_runtime_put(&p->pdev->dev); - } - } + /* Configure pins before asserting CS */ + sh_msiof_spi_set_pin_regs(p, !!(spi->mode & SPI_CPOL), + !!(spi->mode & SPI_CPHA), + !!(spi->mode & SPI_3WIRE), + !!(spi->mode & SPI_LSB_FIRST), + !!(spi->mode & SPI_CS_HIGH)); + return 0; } static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, @@ -487,7 +508,7 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, /* clear status bits */ sh_msiof_reset_str(p); - /* shut down frame, tx/tx and clock signals */ + /* shut down frame, rx/tx and clock signals */ ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0); ret = ret ? ret : sh_msiof_modify_ctr_wait(p, CTR_TXE, 0); if (rx_buf) @@ -505,9 +526,11 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, return ret; } -static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t) +static int sh_msiof_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) { - struct sh_msiof_spi_priv *p = spi_master_get_devdata(spi->master); + struct sh_msiof_spi_priv *p = spi_master_get_devdata(master); void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int); void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int); int bits; @@ -517,7 +540,7 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t) int n; bool swab; - bits = sh_msiof_spi_bits(spi, t); + bits = t->bits_per_word; if (bits <= 8 && t->len > 15 && !(t->len & 3)) { bits = 32; @@ -567,8 +590,7 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t) } /* setup clocks (clock already enabled in chipselect()) */ - sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), - sh_msiof_spi_hz(spi, t)); + sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz); /* transfer in fifo sized chunks */ words = t->len / bytes_per_word; @@ -588,22 +610,36 @@ static int sh_msiof_spi_txrx(struct spi_device *spi, struct spi_transfer *t) words -= n; } - return bytes_done; -} - -static u32 sh_msiof_spi_txrx_word(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits) -{ - BUG(); /* unused but needed by bitbang code */ return 0; } +static const struct sh_msiof_chipdata sh_data = { + .tx_fifo_size = 64, + .rx_fifo_size = 64, + .master_flags = 0, +}; + +static const struct sh_msiof_chipdata r8a779x_data = { + .tx_fifo_size = 64, + .rx_fifo_size = 256, + .master_flags = SPI_MASTER_MUST_TX, +}; + +static const struct of_device_id sh_msiof_match[] = { + { .compatible = "renesas,sh-msiof", .data = &sh_data }, + { .compatible = "renesas,sh-mobile-msiof", .data = &sh_data }, + { .compatible = "renesas,msiof-r8a7790", .data = &r8a779x_data }, + { .compatible = "renesas,msiof-r8a7791", .data = &r8a779x_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, sh_msiof_match); + #ifdef CONFIG_OF static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev) { struct sh_msiof_spi_info *info; struct device_node *np = dev->of_node; - u32 num_cs = 0; + u32 num_cs = 1; info = devm_kzalloc(dev, sizeof(struct sh_msiof_spi_info), GFP_KERNEL); if (!info) { @@ -633,6 +669,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) { struct resource *r; struct spi_master *master; + const struct of_device_id *of_id; struct sh_msiof_spi_priv *p; int i; int ret; @@ -646,10 +683,15 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) p = spi_master_get_devdata(master); platform_set_drvdata(pdev, p); - if (pdev->dev.of_node) + + of_id = of_match_device(sh_msiof_match, &pdev->dev); + if (of_id) { + p->chipdata = of_id->data; p->info = sh_msiof_spi_parse_dt(&pdev->dev); - else + } else { + p->chipdata = (const void *)pdev->id_entry->driver_data; p->info = dev_get_platdata(&pdev->dev); + } if (!p->info) { dev_err(&pdev->dev, "failed to obtain device info\n"); @@ -687,49 +729,40 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) goto err1; } - ret = clk_prepare(p->clk); - if (ret < 0) { - dev_err(&pdev->dev, "unable to prepare clock\n"); - goto err1; - } - p->pdev = pdev; pm_runtime_enable(&pdev->dev); - /* The standard version of MSIOF use 64 word FIFOs */ - p->tx_fifo_size = 64; - p->rx_fifo_size = 64; - /* Platform data may override FIFO sizes */ + p->tx_fifo_size = p->chipdata->tx_fifo_size; + p->rx_fifo_size = p->chipdata->rx_fifo_size; if (p->info->tx_fifo_override) p->tx_fifo_size = p->info->tx_fifo_override; if (p->info->rx_fifo_override) p->rx_fifo_size = p->info->rx_fifo_override; - /* init master and bitbang code */ + /* init master code */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->mode_bits |= SPI_LSB_FIRST | SPI_3WIRE; - master->flags = 0; + master->flags = p->chipdata->master_flags; master->bus_num = pdev->id; + master->dev.of_node = pdev->dev.of_node; master->num_chipselect = p->info->num_chipselect; - master->setup = spi_bitbang_setup; - master->cleanup = spi_bitbang_cleanup; - - p->bitbang.master = master; - p->bitbang.chipselect = sh_msiof_spi_chipselect; - p->bitbang.setup_transfer = sh_msiof_spi_setup_transfer; - p->bitbang.txrx_bufs = sh_msiof_spi_txrx; - p->bitbang.txrx_word[SPI_MODE_0] = sh_msiof_spi_txrx_word; - p->bitbang.txrx_word[SPI_MODE_1] = sh_msiof_spi_txrx_word; - p->bitbang.txrx_word[SPI_MODE_2] = sh_msiof_spi_txrx_word; - p->bitbang.txrx_word[SPI_MODE_3] = sh_msiof_spi_txrx_word; - - ret = spi_bitbang_start(&p->bitbang); - if (ret == 0) - return 0; + master->setup = sh_msiof_spi_setup; + master->prepare_message = sh_msiof_prepare_message; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32); + master->auto_runtime_pm = true; + master->transfer_one = sh_msiof_transfer_one; + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret < 0) { + dev_err(&pdev->dev, "spi_register_master error.\n"); + goto err2; + } + return 0; + + err2: pm_runtime_disable(&pdev->dev); - clk_unprepare(p->clk); err1: spi_master_put(master); return ret; @@ -737,30 +770,22 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) static int sh_msiof_spi_remove(struct platform_device *pdev) { - struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev); - int ret; - - ret = spi_bitbang_stop(&p->bitbang); - if (!ret) { - pm_runtime_disable(&pdev->dev); - clk_unprepare(p->clk); - spi_master_put(p->bitbang.master); - } - return ret; + pm_runtime_disable(&pdev->dev); + return 0; } -#ifdef CONFIG_OF -static const struct of_device_id sh_msiof_match[] = { - { .compatible = "renesas,sh-msiof", }, - { .compatible = "renesas,sh-mobile-msiof", }, +static struct platform_device_id spi_driver_ids[] = { + { "spi_sh_msiof", (kernel_ulong_t)&sh_data }, + { "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data }, + { "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data }, {}, }; -MODULE_DEVICE_TABLE(of, sh_msiof_match); -#endif +MODULE_DEVICE_TABLE(platform, spi_driver_ids); static struct platform_driver sh_msiof_spi_drv = { .probe = sh_msiof_spi_probe, .remove = sh_msiof_spi_remove, + .id_table = spi_driver_ids, .driver = { .name = "spi_sh_msiof", .owner = THIS_MODULE, diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c index 38eb24df796c..8b44b71f5024 100644 --- a/drivers/spi/spi-sh-sci.c +++ b/drivers/spi/spi-sh-sci.c @@ -14,7 +14,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/workqueue.h> @@ -109,7 +108,7 @@ static void sh_sci_spi_chipselect(struct spi_device *dev, int value) { struct sh_sci_spi *sp = spi_master_get_devdata(dev->master); - if (sp->info && sp->info->chip_select) + if (sp->info->chip_select) (sp->info->chip_select)(sp->info, dev->chip_select, value); } @@ -131,6 +130,11 @@ static int sh_sci_spi_probe(struct platform_device *dev) platform_set_drvdata(dev, sp); sp->info = dev_get_platdata(&dev->dev); + if (!sp->info) { + dev_err(&dev->dev, "platform data is missing\n"); + ret = -ENOENT; + goto err1; + } /* setup spi bitbang adaptor */ sp->bitbang.master = master; diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index e430689c3837..1a77ad52812f 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -22,7 +22,6 @@ #include <linux/dmaengine.h> #include <linux/dma-direction.h> #include <linux/dma-mapping.h> -#include <linux/sirfsoc_dma.h> #define DRIVER_NAME "sirfsoc_spi" @@ -132,6 +131,8 @@ #define IS_DMA_VALID(x) (x && ALIGNED(x->tx_buf) && ALIGNED(x->rx_buf) && \ ALIGNED(x->len) && (x->len < 2 * PAGE_SIZE)) +#define SIRFSOC_MAX_CMD_BYTES 4 + struct sirfsoc_spi { struct spi_bitbang bitbang; struct completion rx_done; @@ -162,6 +163,12 @@ struct sirfsoc_spi { void *dummypage; int word_width; /* in bytes */ + /* + * if tx size is not more than 4 and rx size is NULL, use + * command model + */ + bool tx_by_cmd; + int chipselect[0]; }; @@ -260,6 +267,12 @@ static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id) writel(spi_stat, sspi->base + SIRFSOC_SPI_INT_STATUS); + if (sspi->tx_by_cmd && (spi_stat & SIRFSOC_SPI_FRM_END)) { + complete(&sspi->tx_done); + writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); + return IRQ_HANDLED; + } + /* Error Conditions */ if (spi_stat & SIRFSOC_SPI_RX_OFLOW || spi_stat & SIRFSOC_SPI_TX_UFLOW) { @@ -310,6 +323,34 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS); + /* + * fill tx_buf into command register and wait for its completion + */ + if (sspi->tx_by_cmd) { + u32 cmd; + memcpy(&cmd, sspi->tx, t->len); + + if (sspi->word_width == 1 && !(spi->mode & SPI_LSB_FIRST)) + cmd = cpu_to_be32(cmd) >> + ((SIRFSOC_MAX_CMD_BYTES - t->len) * 8); + if (sspi->word_width == 2 && t->len == 4 && + (!(spi->mode & SPI_LSB_FIRST))) + cmd = ((cmd & 0xffff) << 16) | (cmd >> 16); + + writel(cmd, sspi->base + SIRFSOC_SPI_CMD); + writel(SIRFSOC_SPI_FRM_END_INT_EN, + sspi->base + SIRFSOC_SPI_INT_EN); + writel(SIRFSOC_SPI_CMD_TX_EN, + sspi->base + SIRFSOC_SPI_TX_RX_EN); + + if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) { + dev_err(&spi->dev, "transfer timeout\n"); + return 0; + } + + return t->len; + } + if (sspi->left_tx_word == 1) { writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | SIRFSOC_SPI_ENA_AUTO_CLR, @@ -459,11 +500,6 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_8; sspi->rx_word = spi_sirfsoc_rx_word_u8; sspi->tx_word = spi_sirfsoc_tx_word_u8; - txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - SIRFSOC_SPI_FIFO_WIDTH_BYTE; - rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - SIRFSOC_SPI_FIFO_WIDTH_BYTE; - sspi->word_width = 1; break; case 12: case 16: @@ -471,26 +507,22 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) SIRFSOC_SPI_TRAN_DAT_FORMAT_16; sspi->rx_word = spi_sirfsoc_rx_word_u16; sspi->tx_word = spi_sirfsoc_tx_word_u16; - txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - SIRFSOC_SPI_FIFO_WIDTH_WORD; - rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - SIRFSOC_SPI_FIFO_WIDTH_WORD; - sspi->word_width = 2; break; case 32: regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_32; sspi->rx_word = spi_sirfsoc_rx_word_u32; sspi->tx_word = spi_sirfsoc_tx_word_u32; - txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - SIRFSOC_SPI_FIFO_WIDTH_DWORD; - rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - SIRFSOC_SPI_FIFO_WIDTH_DWORD; - sspi->word_width = 4; break; default: BUG(); } + sspi->word_width = DIV_ROUND_UP(bits_per_word, 8); + txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | + sspi->word_width; + rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | + sspi->word_width; + if (!(spi->mode & SPI_CS_HIGH)) regval |= SIRFSOC_SPI_CS_IDLE_STAT; if (!(spi->mode & SPI_LSB_FIRST)) @@ -519,6 +551,14 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) writel(txfifo_ctrl, sspi->base + SIRFSOC_SPI_TXFIFO_CTRL); writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL); + if (t && t->tx_buf && !t->rx_buf && (t->len <= SIRFSOC_MAX_CMD_BYTES)) { + regval |= (SIRFSOC_SPI_CMD_BYTE_NUM((t->len - 1)) | + SIRFSOC_SPI_CMD_MODE); + sspi->tx_by_cmd = true; + } else { + regval &= ~SIRFSOC_SPI_CMD_MODE; + sspi->tx_by_cmd = false; + } writel(regval, sspi->base + SIRFSOC_SPI_CTRL); if (IS_DMA_VALID(t)) { @@ -548,8 +588,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) struct spi_master *master; struct resource *mem_res; int num_cs, cs_gpio, irq; - u32 rx_dma_ch, tx_dma_ch; - dma_cap_mask_t dma_cap_mask; int i; int ret; @@ -560,20 +598,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) goto err_cs; } - ret = of_property_read_u32(pdev->dev.of_node, - "sirf,spi-dma-rx-channel", &rx_dma_ch); - if (ret < 0) { - dev_err(&pdev->dev, "Unable to get rx dma channel\n"); - goto err_cs; - } - - ret = of_property_read_u32(pdev->dev.of_node, - "sirf,spi-dma-tx-channel", &tx_dma_ch); - if (ret < 0) { - dev_err(&pdev->dev, "Unable to get tx dma channel\n"); - goto err_cs; - } - master = spi_alloc_master(&pdev->dev, sizeof(*sspi) + sizeof(int) * num_cs); if (!master) { dev_err(&pdev->dev, "Unable to allocate SPI master\n"); @@ -637,18 +661,13 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) sspi->bitbang.master->dev.of_node = pdev->dev.of_node; /* request DMA channels */ - dma_cap_zero(dma_cap_mask); - dma_cap_set(DMA_INTERLEAVE, dma_cap_mask); - - sspi->rx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id, - (void *)rx_dma_ch); + sspi->rx_chan = dma_request_slave_channel(&pdev->dev, "rx"); if (!sspi->rx_chan) { dev_err(&pdev->dev, "can not allocate rx dma channel\n"); ret = -ENODEV; goto free_master; } - sspi->tx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id, - (void *)tx_dma_ch); + sspi->tx_chan = dma_request_slave_channel(&pdev->dev, "tx"); if (!sspi->tx_chan) { dev_err(&pdev->dev, "can not allocate tx dma channel\n"); ret = -ENODEV; @@ -724,11 +743,16 @@ static int spi_sirfsoc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int spi_sirfsoc_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct sirfsoc_spi *sspi = spi_master_get_devdata(master); + int ret; + + ret = spi_master_suspend(master); + if (ret) + return ret; clk_disable(sspi->clk); return 0; @@ -745,15 +769,13 @@ static int spi_sirfsoc_resume(struct device *dev) writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - return 0; + return spi_master_resume(master); } - -static const struct dev_pm_ops spi_sirfsoc_pm_ops = { - .suspend = spi_sirfsoc_suspend, - .resume = spi_sirfsoc_resume, -}; #endif +static SIMPLE_DEV_PM_OPS(spi_sirfsoc_pm_ops, spi_sirfsoc_suspend, + spi_sirfsoc_resume); + static const struct of_device_id spi_sirfsoc_of_match[] = { { .compatible = "sirf,prima2-spi", }, { .compatible = "sirf,marco-spi", }, @@ -765,9 +787,7 @@ static struct platform_driver spi_sirfsoc_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, -#ifdef CONFIG_PM .pm = &spi_sirfsoc_pm_ops, -#endif .of_match_table = spi_sirfsoc_of_match, }, .probe = spi_sirfsoc_probe, diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c new file mode 100644 index 000000000000..d266a8702067 --- /dev/null +++ b/drivers/spi/spi-sun4i.c @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2012 - 2014 Allwinner Tech + * Pan Nan <pannan@allwinnertech.com> + * + * Copyright (C) 2014 Maxime Ripard + * Maxime Ripard <maxime.ripard@free-electrons.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/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/workqueue.h> + +#include <linux/spi/spi.h> + +#define SUN4I_FIFO_DEPTH 64 + +#define SUN4I_RXDATA_REG 0x00 + +#define SUN4I_TXDATA_REG 0x04 + +#define SUN4I_CTL_REG 0x08 +#define SUN4I_CTL_ENABLE BIT(0) +#define SUN4I_CTL_MASTER BIT(1) +#define SUN4I_CTL_CPHA BIT(2) +#define SUN4I_CTL_CPOL BIT(3) +#define SUN4I_CTL_CS_ACTIVE_LOW BIT(4) +#define SUN4I_CTL_LMTF BIT(6) +#define SUN4I_CTL_TF_RST BIT(8) +#define SUN4I_CTL_RF_RST BIT(9) +#define SUN4I_CTL_XCH BIT(10) +#define SUN4I_CTL_CS_MASK 0x3000 +#define SUN4I_CTL_CS(cs) (((cs) << 12) & SUN4I_CTL_CS_MASK) +#define SUN4I_CTL_DHB BIT(15) +#define SUN4I_CTL_CS_MANUAL BIT(16) +#define SUN4I_CTL_CS_LEVEL BIT(17) +#define SUN4I_CTL_TP BIT(18) + +#define SUN4I_INT_CTL_REG 0x0c +#define SUN4I_INT_CTL_TC BIT(16) + +#define SUN4I_INT_STA_REG 0x10 + +#define SUN4I_DMA_CTL_REG 0x14 + +#define SUN4I_WAIT_REG 0x18 + +#define SUN4I_CLK_CTL_REG 0x1c +#define SUN4I_CLK_CTL_CDR2_MASK 0xff +#define SUN4I_CLK_CTL_CDR2(div) ((div) & SUN4I_CLK_CTL_CDR2_MASK) +#define SUN4I_CLK_CTL_CDR1_MASK 0xf +#define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8) +#define SUN4I_CLK_CTL_DRS BIT(12) + +#define SUN4I_BURST_CNT_REG 0x20 +#define SUN4I_BURST_CNT(cnt) ((cnt) & 0xffffff) + +#define SUN4I_XMIT_CNT_REG 0x24 +#define SUN4I_XMIT_CNT(cnt) ((cnt) & 0xffffff) + +#define SUN4I_FIFO_STA_REG 0x28 +#define SUN4I_FIFO_STA_RF_CNT_MASK 0x7f +#define SUN4I_FIFO_STA_RF_CNT_BITS 0 +#define SUN4I_FIFO_STA_TF_CNT_MASK 0x7f +#define SUN4I_FIFO_STA_TF_CNT_BITS 16 + +struct sun4i_spi { + struct spi_master *master; + void __iomem *base_addr; + struct clk *hclk; + struct clk *mclk; + + struct completion done; + + const u8 *tx_buf; + u8 *rx_buf; + int len; +}; + +static inline u32 sun4i_spi_read(struct sun4i_spi *sspi, u32 reg) +{ + return readl(sspi->base_addr + reg); +} + +static inline void sun4i_spi_write(struct sun4i_spi *sspi, u32 reg, u32 value) +{ + writel(value, sspi->base_addr + reg); +} + +static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len) +{ + u32 reg, cnt; + u8 byte; + + /* See how much data is available */ + reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG); + reg &= SUN4I_FIFO_STA_RF_CNT_MASK; + cnt = reg >> SUN4I_FIFO_STA_RF_CNT_BITS; + + if (len > cnt) + len = cnt; + + while (len--) { + byte = readb(sspi->base_addr + SUN4I_RXDATA_REG); + if (sspi->rx_buf) + *sspi->rx_buf++ = byte; + } +} + +static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len) +{ + u8 byte; + + if (len > sspi->len) + len = sspi->len; + + while (len--) { + byte = sspi->tx_buf ? *sspi->tx_buf++ : 0; + writeb(byte, sspi->base_addr + SUN4I_TXDATA_REG); + sspi->len--; + } +} + +static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct sun4i_spi *sspi = spi_master_get_devdata(spi->master); + u32 reg; + + reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); + + reg &= ~SUN4I_CTL_CS_MASK; + reg |= SUN4I_CTL_CS(spi->chip_select); + + if (enable) + reg |= SUN4I_CTL_CS_LEVEL; + else + reg &= ~SUN4I_CTL_CS_LEVEL; + + /* + * Even though this looks irrelevant since we are supposed to + * be controlling the chip select manually, this bit also + * controls the levels of the chip select for inactive + * devices. + * + * If we don't set it, the chip select level will go low by + * default when the device is idle, which is not really + * expected in the common case where the chip select is active + * low. + */ + if (spi->mode & SPI_CS_HIGH) + reg &= ~SUN4I_CTL_CS_ACTIVE_LOW; + else + reg |= SUN4I_CTL_CS_ACTIVE_LOW; + + sun4i_spi_write(sspi, SUN4I_CTL_REG, reg); +} + +static int sun4i_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct sun4i_spi *sspi = spi_master_get_devdata(master); + unsigned int mclk_rate, div, timeout; + unsigned int tx_len = 0; + int ret = 0; + u32 reg; + + /* We don't support transfer larger than the FIFO */ + if (tfr->len > SUN4I_FIFO_DEPTH) + return -EINVAL; + + reinit_completion(&sspi->done); + sspi->tx_buf = tfr->tx_buf; + sspi->rx_buf = tfr->rx_buf; + sspi->len = tfr->len; + + /* Clear pending interrupts */ + sun4i_spi_write(sspi, SUN4I_INT_STA_REG, ~0); + + + reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); + + /* Reset FIFOs */ + sun4i_spi_write(sspi, SUN4I_CTL_REG, + reg | SUN4I_CTL_RF_RST | SUN4I_CTL_TF_RST); + + /* + * Setup the transfer control register: Chip Select, + * polarities, etc. + */ + if (spi->mode & SPI_CPOL) + reg |= SUN4I_CTL_CPOL; + else + reg &= ~SUN4I_CTL_CPOL; + + if (spi->mode & SPI_CPHA) + reg |= SUN4I_CTL_CPHA; + else + reg &= ~SUN4I_CTL_CPHA; + + if (spi->mode & SPI_LSB_FIRST) + reg |= SUN4I_CTL_LMTF; + else + reg &= ~SUN4I_CTL_LMTF; + + + /* + * If it's a TX only transfer, we don't want to fill the RX + * FIFO with bogus data + */ + if (sspi->rx_buf) + reg &= ~SUN4I_CTL_DHB; + else + reg |= SUN4I_CTL_DHB; + + /* We want to control the chip select manually */ + reg |= SUN4I_CTL_CS_MANUAL; + + sun4i_spi_write(sspi, SUN4I_CTL_REG, reg); + + /* Ensure that we have a parent clock fast enough */ + mclk_rate = clk_get_rate(sspi->mclk); + if (mclk_rate < (2 * spi->max_speed_hz)) { + clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); + mclk_rate = clk_get_rate(sspi->mclk); + } + + /* + * Setup clock divider. + * + * We have two choices there. Either we can use the clock + * divide rate 1, which is calculated thanks to this formula: + * SPI_CLK = MOD_CLK / (2 ^ (cdr + 1)) + * Or we can use CDR2, which is calculated with the formula: + * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) + * Wether we use the former or the latter is set through the + * DRS bit. + * + * First try CDR2, and if we can't reach the expected + * frequency, fall back to CDR1. + */ + div = mclk_rate / (2 * spi->max_speed_hz); + if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) { + if (div > 0) + div--; + + reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS; + } else { + div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); + reg = SUN4I_CLK_CTL_CDR1(div); + } + + sun4i_spi_write(sspi, SUN4I_CLK_CTL_REG, reg); + + /* Setup the transfer now... */ + if (sspi->tx_buf) + tx_len = tfr->len; + + /* Setup the counters */ + sun4i_spi_write(sspi, SUN4I_BURST_CNT_REG, SUN4I_BURST_CNT(tfr->len)); + sun4i_spi_write(sspi, SUN4I_XMIT_CNT_REG, SUN4I_XMIT_CNT(tx_len)); + + /* Fill the TX FIFO */ + sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH); + + /* Enable the interrupts */ + sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC); + + /* Start the transfer */ + reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); + sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH); + + timeout = wait_for_completion_timeout(&sspi->done, + msecs_to_jiffies(1000)); + if (!timeout) { + ret = -ETIMEDOUT; + goto out; + } + + sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); + +out: + sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0); + + return ret; +} + +static irqreturn_t sun4i_spi_handler(int irq, void *dev_id) +{ + struct sun4i_spi *sspi = dev_id; + u32 status = sun4i_spi_read(sspi, SUN4I_INT_STA_REG); + + /* Transfer complete */ + if (status & SUN4I_INT_CTL_TC) { + sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC); + complete(&sspi->done); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int sun4i_spi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct sun4i_spi *sspi = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(sspi->hclk); + if (ret) { + dev_err(dev, "Couldn't enable AHB clock\n"); + goto out; + } + + ret = clk_prepare_enable(sspi->mclk); + if (ret) { + dev_err(dev, "Couldn't enable module clock\n"); + goto err; + } + + sun4i_spi_write(sspi, SUN4I_CTL_REG, + SUN4I_CTL_ENABLE | SUN4I_CTL_MASTER | SUN4I_CTL_TP); + + return 0; + +err: + clk_disable_unprepare(sspi->hclk); +out: + return ret; +} + +static int sun4i_spi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct sun4i_spi *sspi = spi_master_get_devdata(master); + + clk_disable_unprepare(sspi->mclk); + clk_disable_unprepare(sspi->hclk); + + return 0; +} + +static int sun4i_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct sun4i_spi *sspi; + struct resource *res; + int ret = 0, irq; + + master = spi_alloc_master(&pdev->dev, sizeof(struct sun4i_spi)); + if (!master) { + dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, master); + sspi = spi_master_get_devdata(master); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sspi->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sspi->base_addr)) { + ret = PTR_ERR(sspi->base_addr); + goto err_free_master; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "No spi IRQ specified\n"); + ret = -ENXIO; + goto err_free_master; + } + + ret = devm_request_irq(&pdev->dev, irq, sun4i_spi_handler, + 0, "sun4i-spi", sspi); + if (ret) { + dev_err(&pdev->dev, "Cannot request IRQ\n"); + goto err_free_master; + } + + sspi->master = master; + master->set_cs = sun4i_spi_set_cs; + master->transfer_one = sun4i_spi_transfer_one; + master->num_chipselect = 4; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; + + sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(sspi->hclk)) { + dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); + ret = PTR_ERR(sspi->hclk); + goto err_free_master; + } + + sspi->mclk = devm_clk_get(&pdev->dev, "mod"); + if (IS_ERR(sspi->mclk)) { + dev_err(&pdev->dev, "Unable to acquire module clock\n"); + ret = PTR_ERR(sspi->mclk); + goto err_free_master; + } + + init_completion(&sspi->done); + + /* + * This wake-up/shutdown pattern is to be able to have the + * device woken up, even if runtime_pm is disabled + */ + ret = sun4i_spi_runtime_resume(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't resume the device\n"); + goto err_free_master; + } + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&pdev->dev, "cannot register SPI master\n"); + goto err_pm_disable; + } + + return 0; + +err_pm_disable: + pm_runtime_disable(&pdev->dev); + sun4i_spi_runtime_suspend(&pdev->dev); +err_free_master: + spi_master_put(master); + return ret; +} + +static int sun4i_spi_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id sun4i_spi_match[] = { + { .compatible = "allwinner,sun4i-a10-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, sun4i_spi_match); + +static const struct dev_pm_ops sun4i_spi_pm_ops = { + .runtime_resume = sun4i_spi_runtime_resume, + .runtime_suspend = sun4i_spi_runtime_suspend, +}; + +static struct platform_driver sun4i_spi_driver = { + .probe = sun4i_spi_probe, + .remove = sun4i_spi_remove, + .driver = { + .name = "sun4i-spi", + .owner = THIS_MODULE, + .of_match_table = sun4i_spi_match, + .pm = &sun4i_spi_pm_ops, + }, +}; +module_platform_driver(sun4i_spi_driver); + +MODULE_AUTHOR("Pan Nan <pannan@allwinnertech.com>"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_DESCRIPTION("Allwinner A1X/A20 SPI controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c new file mode 100644 index 000000000000..b3e3498a7e6f --- /dev/null +++ b/drivers/spi/spi-sun6i.c @@ -0,0 +1,484 @@ +/* + * Copyright (C) 2012 - 2014 Allwinner Tech + * Pan Nan <pannan@allwinnertech.com> + * + * Copyright (C) 2014 Maxime Ripard + * Maxime Ripard <maxime.ripard@free-electrons.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/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/workqueue.h> + +#include <linux/spi/spi.h> + +#define SUN6I_FIFO_DEPTH 128 + +#define SUN6I_GBL_CTL_REG 0x04 +#define SUN6I_GBL_CTL_BUS_ENABLE BIT(0) +#define SUN6I_GBL_CTL_MASTER BIT(1) +#define SUN6I_GBL_CTL_TP BIT(7) +#define SUN6I_GBL_CTL_RST BIT(31) + +#define SUN6I_TFR_CTL_REG 0x08 +#define SUN6I_TFR_CTL_CPHA BIT(0) +#define SUN6I_TFR_CTL_CPOL BIT(1) +#define SUN6I_TFR_CTL_SPOL BIT(2) +#define SUN6I_TFR_CTL_CS_MASK 0x30 +#define SUN6I_TFR_CTL_CS(cs) (((cs) << 4) & SUN6I_TFR_CTL_CS_MASK) +#define SUN6I_TFR_CTL_CS_MANUAL BIT(6) +#define SUN6I_TFR_CTL_CS_LEVEL BIT(7) +#define SUN6I_TFR_CTL_DHB BIT(8) +#define SUN6I_TFR_CTL_FBS BIT(12) +#define SUN6I_TFR_CTL_XCH BIT(31) + +#define SUN6I_INT_CTL_REG 0x10 +#define SUN6I_INT_CTL_RF_OVF BIT(8) +#define SUN6I_INT_CTL_TC BIT(12) + +#define SUN6I_INT_STA_REG 0x14 + +#define SUN6I_FIFO_CTL_REG 0x18 +#define SUN6I_FIFO_CTL_RF_RST BIT(15) +#define SUN6I_FIFO_CTL_TF_RST BIT(31) + +#define SUN6I_FIFO_STA_REG 0x1c +#define SUN6I_FIFO_STA_RF_CNT_MASK 0x7f +#define SUN6I_FIFO_STA_RF_CNT_BITS 0 +#define SUN6I_FIFO_STA_TF_CNT_MASK 0x7f +#define SUN6I_FIFO_STA_TF_CNT_BITS 16 + +#define SUN6I_CLK_CTL_REG 0x24 +#define SUN6I_CLK_CTL_CDR2_MASK 0xff +#define SUN6I_CLK_CTL_CDR2(div) (((div) & SUN6I_CLK_CTL_CDR2_MASK) << 0) +#define SUN6I_CLK_CTL_CDR1_MASK 0xf +#define SUN6I_CLK_CTL_CDR1(div) (((div) & SUN6I_CLK_CTL_CDR1_MASK) << 8) +#define SUN6I_CLK_CTL_DRS BIT(12) + +#define SUN6I_BURST_CNT_REG 0x30 +#define SUN6I_BURST_CNT(cnt) ((cnt) & 0xffffff) + +#define SUN6I_XMIT_CNT_REG 0x34 +#define SUN6I_XMIT_CNT(cnt) ((cnt) & 0xffffff) + +#define SUN6I_BURST_CTL_CNT_REG 0x38 +#define SUN6I_BURST_CTL_CNT_STC(cnt) ((cnt) & 0xffffff) + +#define SUN6I_TXDATA_REG 0x200 +#define SUN6I_RXDATA_REG 0x300 + +struct sun6i_spi { + struct spi_master *master; + void __iomem *base_addr; + struct clk *hclk; + struct clk *mclk; + struct reset_control *rstc; + + struct completion done; + + const u8 *tx_buf; + u8 *rx_buf; + int len; +}; + +static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg) +{ + return readl(sspi->base_addr + reg); +} + +static inline void sun6i_spi_write(struct sun6i_spi *sspi, u32 reg, u32 value) +{ + writel(value, sspi->base_addr + reg); +} + +static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len) +{ + u32 reg, cnt; + u8 byte; + + /* See how much data is available */ + reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); + reg &= SUN6I_FIFO_STA_RF_CNT_MASK; + cnt = reg >> SUN6I_FIFO_STA_RF_CNT_BITS; + + if (len > cnt) + len = cnt; + + while (len--) { + byte = readb(sspi->base_addr + SUN6I_RXDATA_REG); + if (sspi->rx_buf) + *sspi->rx_buf++ = byte; + } +} + +static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len) +{ + u8 byte; + + if (len > sspi->len) + len = sspi->len; + + while (len--) { + byte = sspi->tx_buf ? *sspi->tx_buf++ : 0; + writeb(byte, sspi->base_addr + SUN6I_TXDATA_REG); + sspi->len--; + } +} + +static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct sun6i_spi *sspi = spi_master_get_devdata(spi->master); + u32 reg; + + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); + reg &= ~SUN6I_TFR_CTL_CS_MASK; + reg |= SUN6I_TFR_CTL_CS(spi->chip_select); + + if (enable) + reg |= SUN6I_TFR_CTL_CS_LEVEL; + else + reg &= ~SUN6I_TFR_CTL_CS_LEVEL; + + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); +} + + +static int sun6i_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct sun6i_spi *sspi = spi_master_get_devdata(master); + unsigned int mclk_rate, div, timeout; + unsigned int tx_len = 0; + int ret = 0; + u32 reg; + + /* We don't support transfer larger than the FIFO */ + if (tfr->len > SUN6I_FIFO_DEPTH) + return -EINVAL; + + reinit_completion(&sspi->done); + sspi->tx_buf = tfr->tx_buf; + sspi->rx_buf = tfr->rx_buf; + sspi->len = tfr->len; + + /* Clear pending interrupts */ + sun6i_spi_write(sspi, SUN6I_INT_STA_REG, ~0); + + /* Reset FIFO */ + sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, + SUN6I_FIFO_CTL_RF_RST | SUN6I_FIFO_CTL_TF_RST); + + /* + * Setup the transfer control register: Chip Select, + * polarities, etc. + */ + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); + + if (spi->mode & SPI_CPOL) + reg |= SUN6I_TFR_CTL_CPOL; + else + reg &= ~SUN6I_TFR_CTL_CPOL; + + if (spi->mode & SPI_CPHA) + reg |= SUN6I_TFR_CTL_CPHA; + else + reg &= ~SUN6I_TFR_CTL_CPHA; + + if (spi->mode & SPI_LSB_FIRST) + reg |= SUN6I_TFR_CTL_FBS; + else + reg &= ~SUN6I_TFR_CTL_FBS; + + /* + * If it's a TX only transfer, we don't want to fill the RX + * FIFO with bogus data + */ + if (sspi->rx_buf) + reg &= ~SUN6I_TFR_CTL_DHB; + else + reg |= SUN6I_TFR_CTL_DHB; + + /* We want to control the chip select manually */ + reg |= SUN6I_TFR_CTL_CS_MANUAL; + + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); + + /* Ensure that we have a parent clock fast enough */ + mclk_rate = clk_get_rate(sspi->mclk); + if (mclk_rate < (2 * spi->max_speed_hz)) { + clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); + mclk_rate = clk_get_rate(sspi->mclk); + } + + /* + * Setup clock divider. + * + * We have two choices there. Either we can use the clock + * divide rate 1, which is calculated thanks to this formula: + * SPI_CLK = MOD_CLK / (2 ^ cdr) + * Or we can use CDR2, which is calculated with the formula: + * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) + * Wether we use the former or the latter is set through the + * DRS bit. + * + * First try CDR2, and if we can't reach the expected + * frequency, fall back to CDR1. + */ + div = mclk_rate / (2 * spi->max_speed_hz); + if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { + if (div > 0) + div--; + + reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS; + } else { + div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); + reg = SUN6I_CLK_CTL_CDR1(div); + } + + sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); + + /* Setup the transfer now... */ + if (sspi->tx_buf) + tx_len = tfr->len; + + /* Setup the counters */ + sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, SUN6I_BURST_CNT(tfr->len)); + sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, SUN6I_XMIT_CNT(tx_len)); + sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, + SUN6I_BURST_CTL_CNT_STC(tx_len)); + + /* Fill the TX FIFO */ + sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH); + + /* Enable the interrupts */ + sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); + + /* Start the transfer */ + reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); + sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH); + + timeout = wait_for_completion_timeout(&sspi->done, + msecs_to_jiffies(1000)); + if (!timeout) { + ret = -ETIMEDOUT; + goto out; + } + + sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH); + +out: + sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0); + + return ret; +} + +static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) +{ + struct sun6i_spi *sspi = dev_id; + u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG); + + /* Transfer complete */ + if (status & SUN6I_INT_CTL_TC) { + sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC); + complete(&sspi->done); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int sun6i_spi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct sun6i_spi *sspi = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(sspi->hclk); + if (ret) { + dev_err(dev, "Couldn't enable AHB clock\n"); + goto out; + } + + ret = clk_prepare_enable(sspi->mclk); + if (ret) { + dev_err(dev, "Couldn't enable module clock\n"); + goto err; + } + + ret = reset_control_deassert(sspi->rstc); + if (ret) { + dev_err(dev, "Couldn't deassert the device from reset\n"); + goto err2; + } + + sun6i_spi_write(sspi, SUN6I_GBL_CTL_REG, + SUN6I_GBL_CTL_BUS_ENABLE | SUN6I_GBL_CTL_MASTER | SUN6I_GBL_CTL_TP); + + return 0; + +err2: + clk_disable_unprepare(sspi->mclk); +err: + clk_disable_unprepare(sspi->hclk); +out: + return ret; +} + +static int sun6i_spi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct sun6i_spi *sspi = spi_master_get_devdata(master); + + reset_control_assert(sspi->rstc); + clk_disable_unprepare(sspi->mclk); + clk_disable_unprepare(sspi->hclk); + + return 0; +} + +static int sun6i_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct sun6i_spi *sspi; + struct resource *res; + int ret = 0, irq; + + master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi)); + if (!master) { + dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, master); + sspi = spi_master_get_devdata(master); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sspi->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sspi->base_addr)) { + ret = PTR_ERR(sspi->base_addr); + goto err_free_master; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "No spi IRQ specified\n"); + ret = -ENXIO; + goto err_free_master; + } + + ret = devm_request_irq(&pdev->dev, irq, sun6i_spi_handler, + 0, "sun6i-spi", sspi); + if (ret) { + dev_err(&pdev->dev, "Cannot request IRQ\n"); + goto err_free_master; + } + + sspi->master = master; + master->set_cs = sun6i_spi_set_cs; + master->transfer_one = sun6i_spi_transfer_one; + master->num_chipselect = 4; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->dev.of_node = pdev->dev.of_node; + master->auto_runtime_pm = true; + + sspi->hclk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(sspi->hclk)) { + dev_err(&pdev->dev, "Unable to acquire AHB clock\n"); + ret = PTR_ERR(sspi->hclk); + goto err_free_master; + } + + sspi->mclk = devm_clk_get(&pdev->dev, "mod"); + if (IS_ERR(sspi->mclk)) { + dev_err(&pdev->dev, "Unable to acquire module clock\n"); + ret = PTR_ERR(sspi->mclk); + goto err_free_master; + } + + init_completion(&sspi->done); + + sspi->rstc = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(sspi->rstc)) { + dev_err(&pdev->dev, "Couldn't get reset controller\n"); + ret = PTR_ERR(sspi->rstc); + goto err_free_master; + } + + /* + * This wake-up/shutdown pattern is to be able to have the + * device woken up, even if runtime_pm is disabled + */ + ret = sun6i_spi_runtime_resume(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't resume the device\n"); + goto err_free_master; + } + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&pdev->dev, "cannot register SPI master\n"); + goto err_pm_disable; + } + + return 0; + +err_pm_disable: + pm_runtime_disable(&pdev->dev); + sun6i_spi_runtime_suspend(&pdev->dev); +err_free_master: + spi_master_put(master); + return ret; +} + +static int sun6i_spi_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id sun6i_spi_match[] = { + { .compatible = "allwinner,sun6i-a31-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, sun6i_spi_match); + +static const struct dev_pm_ops sun6i_spi_pm_ops = { + .runtime_resume = sun6i_spi_runtime_resume, + .runtime_suspend = sun6i_spi_runtime_suspend, +}; + +static struct platform_driver sun6i_spi_driver = { + .probe = sun6i_spi_probe, + .remove = sun6i_spi_remove, + .driver = { + .name = "sun6i-spi", + .owner = THIS_MODULE, + .of_match_table = sun6i_spi_match, + .pm = &sun6i_spi_pm_ops, + }, +}; +module_platform_driver(sun6i_spi_driver); + +MODULE_AUTHOR("Pan Nan <pannan@allwinnertech.com>"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_DESCRIPTION("Allwinner A31 SPI controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 413c71843492..400649595505 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -23,7 +23,6 @@ #include <linux/dma-mapping.h> #include <linux/dmapool.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> @@ -172,7 +171,6 @@ struct tegra_spi_data { void __iomem *base; phys_addr_t phys; unsigned irq; - u32 spi_max_frequency; u32 cur_speed; struct spi_device *cur_spi; @@ -761,11 +759,6 @@ static int tegra_spi_setup(struct spi_device *spi) spi->mode & SPI_CPHA ? "" : "~", spi->max_speed_hz); - BUG_ON(spi->chip_select >= MAX_CHIP_SELECT); - - /* Set speed to the spi max fequency if spi device has not set */ - spi->max_speed_hz = spi->max_speed_hz ? : tspi->spi_max_frequency; - ret = pm_runtime_get_sync(tspi->dev); if (ret < 0) { dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); @@ -853,8 +846,8 @@ complete_xfer: SPI_COMMAND1); tegra_spi_transfer_delay(xfer->delay_usecs); goto exit; - } else if (msg->transfers.prev == &xfer->transfer_list) { - /* This is the last transfer in message */ + } else if (list_is_last(&xfer->transfer_list, + &msg->transfers)) { if (xfer->cs_change) tspi->cs_control = spi; else { @@ -1019,16 +1012,6 @@ static irqreturn_t tegra_spi_isr(int irq, void *context_data) return IRQ_WAKE_THREAD; } -static void tegra_spi_parse_dt(struct platform_device *pdev, - struct tegra_spi_data *tspi) -{ - struct device_node *np = pdev->dev.of_node; - - if (of_property_read_u32(np, "spi-max-frequency", - &tspi->spi_max_frequency)) - tspi->spi_max_frequency = 25000000; /* 25MHz */ -} - static struct of_device_id tegra_spi_of_match[] = { { .compatible = "nvidia,tegra114-spi", }, {} @@ -1050,15 +1033,15 @@ static int tegra_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); tspi = spi_master_get_devdata(master); - /* Parse DT */ - tegra_spi_parse_dt(pdev, tspi); + if (of_property_read_u32(pdev->dev.of_node, "spi-max-frequency", + &master->max_speed_hz)) + master->max_speed_hz = 25000000; /* 25MHz */ /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->setup = tegra_spi_setup; master->transfer_one_message = tegra_spi_transfer_one_message; master->num_chipselect = MAX_CHIP_SELECT; - master->bus_num = -1; master->auto_runtime_pm = true; tspi->master = master; diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 08794977f21a..47869ea636e1 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -22,7 +22,6 @@ #include <linux/completion.h> #include <linux/delay.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> @@ -121,7 +120,6 @@ struct tegra_sflash_data { struct reset_control *rst; void __iomem *base; unsigned irq; - u32 spi_max_frequency; u32 cur_speed; struct spi_device *cur_spi; @@ -315,15 +313,6 @@ static int tegra_sflash_start_transfer_one(struct spi_device *spi, return tegra_sflash_start_cpu_based_transfer(tsd, t); } -static int tegra_sflash_setup(struct spi_device *spi) -{ - struct tegra_sflash_data *tsd = spi_master_get_devdata(spi->master); - - /* Set speed to the spi max fequency if spi device has not set */ - spi->max_speed_hz = spi->max_speed_hz ? : tsd->spi_max_frequency; - return 0; -} - static int tegra_sflash_transfer_one_message(struct spi_master *master, struct spi_message *msg) { @@ -430,15 +419,6 @@ static irqreturn_t tegra_sflash_isr(int irq, void *context_data) return handle_cpu_based_xfer(tsd); } -static void tegra_sflash_parse_dt(struct tegra_sflash_data *tsd) -{ - struct device_node *np = tsd->dev->of_node; - - if (of_property_read_u32(np, "spi-max-frequency", - &tsd->spi_max_frequency)) - tsd->spi_max_frequency = 25000000; /* 25MHz */ -} - static struct of_device_id tegra_sflash_of_match[] = { { .compatible = "nvidia,tegra20-sflash", }, {} @@ -467,11 +447,9 @@ static int tegra_sflash_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA; - master->setup = tegra_sflash_setup; master->transfer_one_message = tegra_sflash_transfer_one_message; master->auto_runtime_pm = true; master->num_chipselect = MAX_CHIP_SELECT; - master->bus_num = -1; platform_set_drvdata(pdev, master); tsd = spi_master_get_devdata(master); @@ -479,7 +457,9 @@ static int tegra_sflash_probe(struct platform_device *pdev) tsd->dev = &pdev->dev; spin_lock_init(&tsd->lock); - tegra_sflash_parse_dt(tsd); + if (of_property_read_u32(tsd->dev->of_node, "spi-max-frequency", + &master->max_speed_hz)) + master->max_speed_hz = 25000000; /* 25MHz */ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); tsd->base = devm_ioremap_resource(&pdev->dev, r); diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index be3a069879c3..e3c1b93e45d1 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -23,7 +23,6 @@ #include <linux/dma-mapping.h> #include <linux/dmapool.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> @@ -171,7 +170,6 @@ struct tegra_slink_data { void __iomem *base; phys_addr_t phys; unsigned irq; - u32 spi_max_frequency; u32 cur_speed; struct spi_device *cur_spi; @@ -761,10 +759,6 @@ static int tegra_slink_setup(struct spi_device *spi) spi->mode & SPI_CPHA ? "" : "~", spi->max_speed_hz); - BUG_ON(spi->chip_select >= MAX_CHIP_SELECT); - - /* Set speed to the spi max fequency if spi device has not set */ - spi->max_speed_hz = spi->max_speed_hz ? : tspi->spi_max_frequency; ret = pm_runtime_get_sync(tspi->dev); if (ret < 0) { dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); @@ -999,15 +993,6 @@ static irqreturn_t tegra_slink_isr(int irq, void *context_data) return IRQ_WAKE_THREAD; } -static void tegra_slink_parse_dt(struct tegra_slink_data *tspi) -{ - struct device_node *np = tspi->dev->of_node; - - if (of_property_read_u32(np, "spi-max-frequency", - &tspi->spi_max_frequency)) - tspi->spi_max_frequency = 25000000; /* 25MHz */ -} - static const struct tegra_slink_chip_data tegra30_spi_cdata = { .cs_hold_time = true, }; @@ -1053,7 +1038,6 @@ static int tegra_slink_probe(struct platform_device *pdev) master->unprepare_message = tegra_slink_unprepare_message; master->auto_runtime_pm = true; master->num_chipselect = MAX_CHIP_SELECT; - master->bus_num = -1; platform_set_drvdata(pdev, master); tspi = spi_master_get_devdata(master); @@ -1062,7 +1046,9 @@ static int tegra_slink_probe(struct platform_device *pdev) tspi->chip_data = cdata; spin_lock_init(&tspi->lock); - tegra_slink_parse_dt(tspi); + if (of_property_read_u32(tspi->dev->of_node, "spi-max-frequency", + &master->max_speed_hz)) + master->max_speed_hz = 25000000; /* 25MHz */ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 3d09265b5133..6c211d1910b0 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -429,13 +429,13 @@ static int ti_qspi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD; - master->bus_num = -1; master->flags = SPI_MASTER_HALF_DUPLEX; master->setup = ti_qspi_setup; master->auto_runtime_pm = true; master->transfer_one_message = ti_qspi_start_transfer_one; master->dev.of_node = pdev->dev.of_node; - master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1); + master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | + SPI_BPW_MASK(8); if (!of_property_read_u32(np, "num-cs", &num_cs)) master->num_chipselect = num_cs; @@ -461,7 +461,6 @@ static int ti_qspi_probe(struct platform_device *pdev) if (res_mmap == NULL) { dev_err(&pdev->dev, "memory mapped resource not required\n"); - return -ENODEV; } } diff --git a/drivers/spi/spi-ti-ssp.c b/drivers/spi/spi-ti-ssp.c deleted file mode 100644 index 7d20e121e4c1..000000000000 --- a/drivers/spi/spi-ti-ssp.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Sequencer Serial Port (SSP) based SPI master driver - * - * Copyright (C) 2010 Texas Instruments 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. - * - * 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/err.h> -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/spi/spi.h> -#include <linux/mfd/ti_ssp.h> - -#define MODE_BITS (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH) - -struct ti_ssp_spi { - struct spi_master *master; - struct device *dev; - spinlock_t lock; - struct list_head msg_queue; - struct completion complete; - bool shutdown; - struct workqueue_struct *workqueue; - struct work_struct work; - u8 mode, bpw; - int cs_active; - u32 pc_en, pc_dis, pc_wr, pc_rd; - void (*select)(int cs); -}; - -static u32 ti_ssp_spi_rx(struct ti_ssp_spi *hw) -{ - u32 ret; - - ti_ssp_run(hw->dev, hw->pc_rd, 0, &ret); - return ret; -} - -static void ti_ssp_spi_tx(struct ti_ssp_spi *hw, u32 data) -{ - ti_ssp_run(hw->dev, hw->pc_wr, data << (32 - hw->bpw), NULL); -} - -static int ti_ssp_spi_txrx(struct ti_ssp_spi *hw, struct spi_message *msg, - struct spi_transfer *t) -{ - int count; - - if (hw->bpw <= 8) { - u8 *rx = t->rx_buf; - const u8 *tx = t->tx_buf; - - for (count = 0; count < t->len; count += 1) { - if (t->tx_buf) - ti_ssp_spi_tx(hw, *tx++); - if (t->rx_buf) - *rx++ = ti_ssp_spi_rx(hw); - } - } else if (hw->bpw <= 16) { - u16 *rx = t->rx_buf; - const u16 *tx = t->tx_buf; - - for (count = 0; count < t->len; count += 2) { - if (t->tx_buf) - ti_ssp_spi_tx(hw, *tx++); - if (t->rx_buf) - *rx++ = ti_ssp_spi_rx(hw); - } - } else { - u32 *rx = t->rx_buf; - const u32 *tx = t->tx_buf; - - for (count = 0; count < t->len; count += 4) { - if (t->tx_buf) - ti_ssp_spi_tx(hw, *tx++); - if (t->rx_buf) - *rx++ = ti_ssp_spi_rx(hw); - } - } - - msg->actual_length += count; /* bytes transferred */ - - dev_dbg(&msg->spi->dev, "xfer %s%s, %d bytes, %d bpw, count %d%s\n", - t->tx_buf ? "tx" : "", t->rx_buf ? "rx" : "", t->len, - hw->bpw, count, (count < t->len) ? " (under)" : ""); - - return (count < t->len) ? -EIO : 0; /* left over data */ -} - -static void ti_ssp_spi_chip_select(struct ti_ssp_spi *hw, int cs_active) -{ - cs_active = !!cs_active; - if (cs_active == hw->cs_active) - return; - ti_ssp_run(hw->dev, cs_active ? hw->pc_en : hw->pc_dis, 0, NULL); - hw->cs_active = cs_active; -} - -#define __SHIFT_OUT(bits) (SSP_OPCODE_SHIFT | SSP_OUT_MODE | \ - cs_en | clk | SSP_COUNT((bits) * 2 - 1)) -#define __SHIFT_IN(bits) (SSP_OPCODE_SHIFT | SSP_IN_MODE | \ - cs_en | clk | SSP_COUNT((bits) * 2 - 1)) - -static int ti_ssp_spi_setup_transfer(struct ti_ssp_spi *hw, u8 bpw, u8 mode) -{ - int error, idx = 0; - u32 seqram[16]; - u32 cs_en, cs_dis, clk; - u32 topbits, botbits; - - mode &= MODE_BITS; - if (mode == hw->mode && bpw == hw->bpw) - return 0; - - cs_en = (mode & SPI_CS_HIGH) ? SSP_CS_HIGH : SSP_CS_LOW; - cs_dis = (mode & SPI_CS_HIGH) ? SSP_CS_LOW : SSP_CS_HIGH; - clk = (mode & SPI_CPOL) ? SSP_CLK_HIGH : SSP_CLK_LOW; - - /* Construct instructions */ - - /* Disable Chip Select */ - hw->pc_dis = idx; - seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_dis | clk; - seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_dis | clk; - - /* Enable Chip Select */ - hw->pc_en = idx; - seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_en | clk; - seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; - - /* Reads and writes need to be split for bpw > 16 */ - topbits = (bpw > 16) ? 16 : bpw; - botbits = bpw - topbits; - - /* Write */ - hw->pc_wr = idx; - seqram[idx++] = __SHIFT_OUT(topbits) | SSP_ADDR_REG; - if (botbits) - seqram[idx++] = __SHIFT_OUT(botbits) | SSP_DATA_REG; - seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; - - /* Read */ - hw->pc_rd = idx; - if (botbits) - seqram[idx++] = __SHIFT_IN(botbits) | SSP_ADDR_REG; - seqram[idx++] = __SHIFT_IN(topbits) | SSP_DATA_REG; - seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; - - error = ti_ssp_load(hw->dev, 0, seqram, idx); - if (error < 0) - return error; - - error = ti_ssp_set_mode(hw->dev, ((mode & SPI_CPHA) ? - 0 : SSP_EARLY_DIN)); - if (error < 0) - return error; - - hw->bpw = bpw; - hw->mode = mode; - - return error; -} - -static void ti_ssp_spi_work(struct work_struct *work) -{ - struct ti_ssp_spi *hw = container_of(work, struct ti_ssp_spi, work); - - spin_lock(&hw->lock); - - while (!list_empty(&hw->msg_queue)) { - struct spi_message *m; - struct spi_device *spi; - struct spi_transfer *t = NULL; - int status = 0; - - m = container_of(hw->msg_queue.next, struct spi_message, - queue); - - list_del_init(&m->queue); - - spin_unlock(&hw->lock); - - spi = m->spi; - - if (hw->select) - hw->select(spi->chip_select); - - list_for_each_entry(t, &m->transfers, transfer_list) { - int bpw = spi->bits_per_word; - int xfer_status; - - if (t->bits_per_word) - bpw = t->bits_per_word; - - if (ti_ssp_spi_setup_transfer(hw, bpw, spi->mode) < 0) - break; - - ti_ssp_spi_chip_select(hw, 1); - - xfer_status = ti_ssp_spi_txrx(hw, m, t); - if (xfer_status < 0) - status = xfer_status; - - if (t->delay_usecs) - udelay(t->delay_usecs); - - if (t->cs_change) - ti_ssp_spi_chip_select(hw, 0); - } - - ti_ssp_spi_chip_select(hw, 0); - m->status = status; - m->complete(m->context); - - spin_lock(&hw->lock); - } - - if (hw->shutdown) - complete(&hw->complete); - - spin_unlock(&hw->lock); -} - -static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m) -{ - struct ti_ssp_spi *hw; - struct spi_transfer *t; - int error = 0; - - m->actual_length = 0; - m->status = -EINPROGRESS; - - hw = spi_master_get_devdata(spi->master); - - if (list_empty(&m->transfers) || !m->complete) - return -EINVAL; - - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->len && !(t->rx_buf || t->tx_buf)) { - dev_err(&spi->dev, "invalid xfer, no buffer\n"); - return -EINVAL; - } - - if (t->len && t->rx_buf && t->tx_buf) { - dev_err(&spi->dev, "invalid xfer, full duplex\n"); - return -EINVAL; - } - } - - spin_lock(&hw->lock); - if (hw->shutdown) { - error = -ESHUTDOWN; - goto error_unlock; - } - list_add_tail(&m->queue, &hw->msg_queue); - queue_work(hw->workqueue, &hw->work); -error_unlock: - spin_unlock(&hw->lock); - return error; -} - -static int ti_ssp_spi_probe(struct platform_device *pdev) -{ - const struct ti_ssp_spi_data *pdata; - struct ti_ssp_spi *hw; - struct spi_master *master; - struct device *dev = &pdev->dev; - int error = 0; - - pdata = dev_get_platdata(dev); - if (!pdata) { - dev_err(dev, "platform data not found\n"); - return -EINVAL; - } - - master = spi_alloc_master(dev, sizeof(struct ti_ssp_spi)); - if (!master) { - dev_err(dev, "cannot allocate SPI master\n"); - return -ENOMEM; - } - - hw = spi_master_get_devdata(master); - platform_set_drvdata(pdev, hw); - - hw->master = master; - hw->dev = dev; - hw->select = pdata->select; - - spin_lock_init(&hw->lock); - init_completion(&hw->complete); - INIT_LIST_HEAD(&hw->msg_queue); - INIT_WORK(&hw->work, ti_ssp_spi_work); - - hw->workqueue = create_singlethread_workqueue(dev_name(dev)); - if (!hw->workqueue) { - error = -ENOMEM; - dev_err(dev, "work queue creation failed\n"); - goto error_wq; - } - - error = ti_ssp_set_iosel(hw->dev, pdata->iosel); - if (error < 0) { - dev_err(dev, "io setup failed\n"); - goto error_iosel; - } - - master->bus_num = pdev->id; - master->num_chipselect = pdata->num_cs; - master->mode_bits = MODE_BITS; - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); - master->flags = SPI_MASTER_HALF_DUPLEX; - master->transfer = ti_ssp_spi_transfer; - - error = spi_register_master(master); - if (error) { - dev_err(dev, "master registration failed\n"); - goto error_reg; - } - - return 0; - -error_reg: -error_iosel: - destroy_workqueue(hw->workqueue); -error_wq: - spi_master_put(master); - return error; -} - -static int ti_ssp_spi_remove(struct platform_device *pdev) -{ - struct ti_ssp_spi *hw = platform_get_drvdata(pdev); - int error; - - hw->shutdown = 1; - while (!list_empty(&hw->msg_queue)) { - error = wait_for_completion_interruptible(&hw->complete); - if (error < 0) { - hw->shutdown = 0; - return error; - } - } - destroy_workqueue(hw->workqueue); - spi_unregister_master(hw->master); - - return 0; -} - -static struct platform_driver ti_ssp_spi_driver = { - .probe = ti_ssp_spi_probe, - .remove = ti_ssp_spi_remove, - .driver = { - .name = "ti-ssp-spi", - .owner = THIS_MODULE, - }, -}; -module_platform_driver(ti_ssp_spi_driver); - -MODULE_DESCRIPTION("SSP SPI Master"); -MODULE_AUTHOR("Cyril Chemparathy"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ti-ssp-spi"); diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 2e7f38c7a961..f406b30af961 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -332,7 +332,7 @@ static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, data->transfer_active = false; wake_up(&data->wait); } else { - dev_err(&data->master->dev, + dev_vdbg(&data->master->dev, "%s : Transfer is not completed", __func__); } @@ -464,20 +464,6 @@ static void pch_spi_reset(struct spi_master *master) pch_spi_writereg(master, PCH_SRST, 0x0); } -static int pch_spi_setup(struct spi_device *pspi) -{ - /* Check baud rate setting */ - /* if baud rate of chip is greater than - max we can support,return error */ - if ((pspi->max_speed_hz) > PCH_MAX_BAUDRATE) - pspi->max_speed_hz = PCH_MAX_BAUDRATE; - - dev_dbg(&pspi->dev, "%s MODE = %x\n", __func__, - (pspi->mode) & (SPI_CPOL | SPI_CPHA)); - - return 0; -} - static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) { @@ -486,23 +472,6 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) int retval; unsigned long flags; - /* validate spi message and baud rate */ - if (unlikely(list_empty(&pmsg->transfers) == 1)) { - dev_err(&pspi->dev, "%s list empty\n", __func__); - retval = -EINVAL; - goto err_out; - } - - if (unlikely(pspi->max_speed_hz == 0)) { - dev_err(&pspi->dev, "%s pch_spi_transfer maxspeed=%d\n", - __func__, pspi->max_speed_hz); - retval = -EINVAL; - goto err_out; - } - - dev_dbg(&pspi->dev, - "%s Transfer List not empty. Transfer Speed is set.\n", __func__); - spin_lock_irqsave(&data->lock, flags); /* validate Tx/Rx buffers and Transfer length */ list_for_each_entry(transfer, &pmsg->transfers, transfer_list) { @@ -523,10 +492,6 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length valid\n", __func__); - - /* if baud rate has been specified validate the same */ - if (transfer->speed_hz > PCH_MAX_BAUDRATE) - transfer->speed_hz = PCH_MAX_BAUDRATE; } spin_unlock_irqrestore(&data->lock, flags); @@ -915,7 +880,7 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw) /* Set Tx DMA */ param = &dma->param_tx; param->dma_dev = &dma_dev->dev; - param->chan_id = data->master->bus_num * 2; /* Tx = 0, 2 */ + param->chan_id = data->ch * 2; /* Tx = 0, 2 */; param->tx_reg = data->io_base_addr + PCH_SPDWR; param->width = width; chan = dma_request_channel(mask, pch_spi_filter, param); @@ -930,7 +895,7 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw) /* Set Rx DMA */ param = &dma->param_rx; param->dma_dev = &dma_dev->dev; - param->chan_id = data->master->bus_num * 2 + 1; /* Rx = Tx + 1 */ + param->chan_id = data->ch * 2 + 1; /* Rx = Tx + 1 */; param->rx_reg = data->io_base_addr + PCH_SPDRR; param->width = width; chan = dma_request_channel(mask, pch_spi_filter, param); @@ -1151,8 +1116,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) dma->nent = num; dma->desc_tx = desc_tx; - dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing " - "0x2 to SSNXCR\n", __func__); + dev_dbg(&data->master->dev, "%s:Pulling down SSN low - writing 0x2 to SSNXCR\n", __func__); spin_lock_irqsave(&data->lock, flags); pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW); @@ -1418,10 +1382,10 @@ static int pch_spi_pd_probe(struct platform_device *plat_dev) /* initialize members of SPI master */ master->num_chipselect = PCH_MAX_CS; - master->setup = pch_spi_setup; master->transfer = pch_spi_transfer; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); + master->max_speed_hz = PCH_MAX_BAUDRATE; data->board_dat = board_dat; data->plat_dev = plat_dev; @@ -1452,6 +1416,11 @@ static int pch_spi_pd_probe(struct platform_device *plat_dev) pch_spi_set_master_mode(master); + if (use_dma) { + dev_info(&plat_dev->dev, "Use DMA for data transfers\n"); + pch_alloc_dma_buf(board_dat, data); + } + ret = spi_register_master(master); if (ret != 0) { dev_err(&plat_dev->dev, @@ -1459,14 +1428,10 @@ static int pch_spi_pd_probe(struct platform_device *plat_dev) goto err_spi_register_master; } - if (use_dma) { - dev_info(&plat_dev->dev, "Use DMA for data transfers\n"); - pch_alloc_dma_buf(board_dat, data); - } - return 0; err_spi_register_master: + pch_free_dma_buf(board_dat, data); free_irq(board_dat->pdev->irq, data); err_request_irq: pch_spi_free_resources(board_dat, data); @@ -1604,8 +1569,7 @@ static struct platform_driver pch_spi_pd_driver = { .resume = pch_spi_pd_resume }; -static int pch_spi_probe(struct pci_dev *pdev, - const struct pci_device_id *id) +static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct pch_spi_board_data *board_dat; struct platform_device *pd_dev = NULL; @@ -1675,6 +1639,8 @@ static int pch_spi_probe(struct pci_dev *pdev, return 0; err_platform_device: + while (--i >= 0) + platform_device_unregister(pd_dev_save->pd_save[i]); pci_disable_device(pdev); pci_enable_device: pci_release_regions(pdev); diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index 6191ced514b2..820b499816f8 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -80,7 +80,6 @@ struct txx9spi { void __iomem *membase; int baseclk; struct clk *clk; - u32 max_speed_hz, min_speed_hz; int last_chipselect; int last_chipselect_val; }; @@ -117,9 +116,7 @@ static int txx9spi_setup(struct spi_device *spi) { struct txx9spi *c = spi_master_get_devdata(spi->master); - if (!spi->max_speed_hz - || spi->max_speed_hz > c->max_speed_hz - || spi->max_speed_hz < c->min_speed_hz) + if (!spi->max_speed_hz) return -EINVAL; if (gpio_direction_output(spi->chip_select, @@ -309,15 +306,8 @@ static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m) /* check each transfer's parameters */ list_for_each_entry(t, &m->transfers, transfer_list) { - u32 speed_hz = t->speed_hz ? : spi->max_speed_hz; - u8 bits_per_word = t->bits_per_word; - if (!t->tx_buf && !t->rx_buf && t->len) return -EINVAL; - if (t->len & ((bits_per_word >> 3) - 1)) - return -EINVAL; - if (speed_hz < c->min_speed_hz || speed_hz > c->max_speed_hz) - return -EINVAL; } spin_lock_irqsave(&c->lock, flags); @@ -360,17 +350,12 @@ static int txx9spi_probe(struct platform_device *dev) goto exit; } c->baseclk = clk_get_rate(c->clk); - c->min_speed_hz = DIV_ROUND_UP(c->baseclk, SPI_MAX_DIVIDER + 1); - c->max_speed_hz = c->baseclk / (SPI_MIN_DIVIDER + 1); + master->min_speed_hz = DIV_ROUND_UP(c->baseclk, SPI_MAX_DIVIDER + 1); + master->max_speed_hz = c->baseclk / (SPI_MIN_DIVIDER + 1); res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) - goto exit_busy; - if (!devm_request_mem_region(&dev->dev, res->start, resource_size(res), - "spi_txx9")) - goto exit_busy; - c->membase = devm_ioremap(&dev->dev, res->start, resource_size(res)); - if (!c->membase) + c->membase = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(c->membase)) goto exit_busy; /* enter config mode */ diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c index 24c40b13dab1..bb478dccf1d8 100644 --- a/drivers/spi/spi-xcomm.c +++ b/drivers/spi/spi-xcomm.c @@ -8,7 +8,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -74,15 +73,13 @@ static void spi_xcomm_chipselect(struct spi_xcomm *spi_xcomm, static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm, struct spi_device *spi, struct spi_transfer *t, unsigned int *settings) { - unsigned int speed; - if (t->len > 62) return -EINVAL; - speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; + if (t->speed_hz != spi_xcomm->current_speed) { + unsigned int divider; - if (speed != spi_xcomm->current_speed) { - unsigned int divider = DIV_ROUND_UP(SPI_XCOMM_CLOCK, speed); + divider = DIV_ROUND_UP(SPI_XCOMM_CLOCK, t->speed_hz); if (divider >= 64) *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_64; else if (divider >= 16) @@ -90,7 +87,7 @@ static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm, else *settings |= SPI_XCOMM_SETTINGS_CLOCK_DIV_4; - spi_xcomm->current_speed = speed; + spi_xcomm->current_speed = t->speed_hz; } if (spi->mode & SPI_CPOL) @@ -148,8 +145,6 @@ static int spi_xcomm_transfer_one(struct spi_master *master, int status = 0; bool is_last; - is_first = true; - spi_xcomm_chipselect(spi_xcomm, spi, true); list_for_each_entry(t, &msg->transfers, transfer_list) { diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 6d4ce4615163..a3b0b9944bf0 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -14,7 +14,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -88,10 +87,10 @@ struct xilinx_spi { const u8 *tx_ptr; /* pointer in the Rx buffer */ int remaining_bytes; /* the number of bytes left to transfer */ u8 bits_per_word; - unsigned int (*read_fn) (void __iomem *); - void (*write_fn) (u32, void __iomem *); - void (*tx_fn) (struct xilinx_spi *); - void (*rx_fn) (struct xilinx_spi *); + 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) @@ -209,26 +208,11 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) } /* spi_bitbang requires custom setup_transfer() to be defined if there is a - * custom txrx_bufs(). We have nothing to setup here as the SPI IP block - * supports 8 or 16 bits per word which cannot be changed in software. - * SPI clock can't be changed in software either. - * Check for correct bits per word. Chip select delay calculations could be - * added here as soon as bitbang_work() can be made aware of the delay value. + * custom txrx_bufs(). */ static int xilinx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { - struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); - u8 bits_per_word; - - bits_per_word = (t && t->bits_per_word) - ? t->bits_per_word : spi->bits_per_word; - if (bits_per_word != xspi->bits_per_word) { - dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", - __func__, bits_per_word); - return -EINVAL; - } - return 0; } @@ -407,6 +391,7 @@ static int xilinx_spi_probe(struct platform_device *pdev) xspi->write_fn = xspi_write32_be; } + 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; diff --git a/drivers/spi/spi-xtensa-xtfpga.c b/drivers/spi/spi-xtensa-xtfpga.c new file mode 100644 index 000000000000..41e158187f9d --- /dev/null +++ b/drivers/spi/spi-xtensa-xtfpga.c @@ -0,0 +1,170 @@ +/* + * Xtensa xtfpga SPI controller driver + * + * Copyright (c) 2014 Cadence Design Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> + +#define XTFPGA_SPI_NAME "xtfpga_spi" + +#define XTFPGA_SPI_START 0x0 +#define XTFPGA_SPI_BUSY 0x4 +#define XTFPGA_SPI_DATA 0x8 + +#define BUSY_WAIT_US 100 + +struct xtfpga_spi { + struct spi_bitbang bitbang; + void __iomem *regs; + u32 data; + unsigned data_sz; +}; + +static inline void xtfpga_spi_write32(const struct xtfpga_spi *spi, + unsigned addr, u32 val) +{ + iowrite32(val, spi->regs + addr); +} + +static inline unsigned int xtfpga_spi_read32(const struct xtfpga_spi *spi, + unsigned addr) +{ + return ioread32(spi->regs + addr); +} + +static inline void xtfpga_spi_wait_busy(struct xtfpga_spi *xspi) +{ + unsigned i; + for (i = 0; xtfpga_spi_read32(xspi, XTFPGA_SPI_BUSY) && + i < BUSY_WAIT_US; ++i) + udelay(1); + WARN_ON_ONCE(i == BUSY_WAIT_US); +} + +static u32 xtfpga_spi_txrx_word(struct spi_device *spi, unsigned nsecs, + u32 v, u8 bits) +{ + struct xtfpga_spi *xspi = spi_master_get_devdata(spi->master); + + xspi->data = (xspi->data << bits) | (v & GENMASK(bits - 1, 0)); + xspi->data_sz += bits; + if (xspi->data_sz >= 16) { + xtfpga_spi_write32(xspi, XTFPGA_SPI_DATA, + xspi->data >> (xspi->data_sz - 16)); + xspi->data_sz -= 16; + xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 1); + xtfpga_spi_wait_busy(xspi); + xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 0); + } + + return 0; +} + +static void xtfpga_spi_chipselect(struct spi_device *spi, int is_on) +{ + struct xtfpga_spi *xspi = spi_master_get_devdata(spi->master); + + WARN_ON(xspi->data_sz != 0); + xspi->data_sz = 0; +} + +static int xtfpga_spi_probe(struct platform_device *pdev) +{ + struct xtfpga_spi *xspi; + struct resource *mem; + int ret; + struct spi_master *master; + + master = spi_alloc_master(&pdev->dev, sizeof(struct xtfpga_spi)); + if (!master) + return -ENOMEM; + + master->flags = SPI_MASTER_NO_RX; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); + master->bus_num = pdev->dev.id; + master->dev.of_node = pdev->dev.of_node; + + xspi = spi_master_get_devdata(master); + xspi->bitbang.master = master; + xspi->bitbang.chipselect = xtfpga_spi_chipselect; + xspi->bitbang.txrx_word[SPI_MODE_0] = xtfpga_spi_txrx_word; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err; + } + xspi->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(xspi->regs)) { + ret = PTR_ERR(xspi->regs); + goto err; + } + + xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 0); + usleep_range(1000, 2000); + if (xtfpga_spi_read32(xspi, XTFPGA_SPI_BUSY)) { + dev_err(&pdev->dev, "Device stuck in busy state\n"); + ret = -EBUSY; + goto err; + } + + ret = spi_bitbang_start(&xspi->bitbang); + if (ret < 0) { + dev_err(&pdev->dev, "spi_bitbang_start failed\n"); + goto err; + } + + platform_set_drvdata(pdev, master); + return 0; +err: + spi_master_put(master); + return ret; +} + +static int xtfpga_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct xtfpga_spi *xspi = spi_master_get_devdata(master); + + spi_bitbang_stop(&xspi->bitbang); + spi_master_put(master); + + return 0; +} + +MODULE_ALIAS("platform:" XTFPGA_SPI_NAME); + +#ifdef CONFIG_OF +static const struct of_device_id xtfpga_spi_of_match[] = { + { .compatible = "cdns,xtfpga-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, xtfpga_spi_of_match); +#endif + +static struct platform_driver xtfpga_spi_driver = { + .probe = xtfpga_spi_probe, + .remove = xtfpga_spi_remove, + .driver = { + .name = XTFPGA_SPI_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(xtfpga_spi_of_match), + }, +}; +module_platform_driver(xtfpga_spi_driver); + +MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>"); +MODULE_DESCRIPTION("xtensa xtfpga SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index d0b28bba38be..4eb9bf02996c 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -24,6 +24,8 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/cache.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> #include <linux/mutex.h> #include <linux/of_device.h> #include <linux/of_irq.h> @@ -255,13 +257,12 @@ EXPORT_SYMBOL_GPL(spi_bus_type); static int spi_drv_probe(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); - struct spi_device *spi = to_spi_device(dev); int ret; - acpi_dev_pm_attach(&spi->dev, true); - ret = sdrv->probe(spi); + acpi_dev_pm_attach(dev, true); + ret = sdrv->probe(to_spi_device(dev)); if (ret) - acpi_dev_pm_detach(&spi->dev, true); + acpi_dev_pm_detach(dev, true); return ret; } @@ -269,11 +270,10 @@ static int spi_drv_probe(struct device *dev) static int spi_drv_remove(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); - struct spi_device *spi = to_spi_device(dev); int ret; - ret = sdrv->remove(spi); - acpi_dev_pm_detach(&spi->dev, true); + ret = sdrv->remove(to_spi_device(dev)); + acpi_dev_pm_detach(dev, true); return ret; } @@ -580,6 +580,169 @@ static void spi_set_cs(struct spi_device *spi, bool enable) spi->master->set_cs(spi, !enable); } +static int spi_map_buf(struct spi_master *master, struct device *dev, + struct sg_table *sgt, void *buf, size_t len, + enum dma_data_direction dir) +{ + const bool vmalloced_buf = is_vmalloc_addr(buf); + const int desc_len = vmalloced_buf ? PAGE_SIZE : master->max_dma_len; + const int sgs = DIV_ROUND_UP(len, desc_len); + struct page *vm_page; + void *sg_buf; + size_t min; + int i, ret; + + ret = sg_alloc_table(sgt, sgs, GFP_KERNEL); + if (ret != 0) + return ret; + + for (i = 0; i < sgs; i++) { + min = min_t(size_t, len, desc_len); + + if (vmalloced_buf) { + vm_page = vmalloc_to_page(buf); + if (!vm_page) { + sg_free_table(sgt); + return -ENOMEM; + } + sg_buf = page_address(vm_page) + + ((size_t)buf & ~PAGE_MASK); + } else { + sg_buf = buf; + } + + sg_set_buf(&sgt->sgl[i], sg_buf, min); + + buf += min; + len -= min; + } + + ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir); + if (ret < 0) { + sg_free_table(sgt); + return ret; + } + + sgt->nents = ret; + + return 0; +} + +static void spi_unmap_buf(struct spi_master *master, struct device *dev, + struct sg_table *sgt, enum dma_data_direction dir) +{ + if (sgt->orig_nents) { + dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir); + sg_free_table(sgt); + } +} + +static int spi_map_msg(struct spi_master *master, struct spi_message *msg) +{ + struct device *tx_dev, *rx_dev; + struct spi_transfer *xfer; + void *tmp; + unsigned int max_tx, max_rx; + int ret; + + if (master->flags & (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX)) { + max_tx = 0; + max_rx = 0; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if ((master->flags & SPI_MASTER_MUST_TX) && + !xfer->tx_buf) + max_tx = max(xfer->len, max_tx); + if ((master->flags & SPI_MASTER_MUST_RX) && + !xfer->rx_buf) + max_rx = max(xfer->len, max_rx); + } + + if (max_tx) { + tmp = krealloc(master->dummy_tx, max_tx, + GFP_KERNEL | GFP_DMA); + if (!tmp) + return -ENOMEM; + master->dummy_tx = tmp; + memset(tmp, 0, max_tx); + } + + if (max_rx) { + tmp = krealloc(master->dummy_rx, max_rx, + GFP_KERNEL | GFP_DMA); + if (!tmp) + return -ENOMEM; + master->dummy_rx = tmp; + } + + if (max_tx || max_rx) { + list_for_each_entry(xfer, &msg->transfers, + transfer_list) { + if (!xfer->tx_buf) + xfer->tx_buf = master->dummy_tx; + if (!xfer->rx_buf) + xfer->rx_buf = master->dummy_rx; + } + } + } + + if (!master->can_dma) + return 0; + + tx_dev = &master->dma_tx->dev->device; + rx_dev = &master->dma_rx->dev->device; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (!master->can_dma(master, msg->spi, xfer)) + continue; + + if (xfer->tx_buf != NULL) { + ret = spi_map_buf(master, tx_dev, &xfer->tx_sg, + (void *)xfer->tx_buf, xfer->len, + DMA_TO_DEVICE); + if (ret != 0) + return ret; + } + + if (xfer->rx_buf != NULL) { + ret = spi_map_buf(master, rx_dev, &xfer->rx_sg, + xfer->rx_buf, xfer->len, + DMA_FROM_DEVICE); + if (ret != 0) { + spi_unmap_buf(master, tx_dev, &xfer->tx_sg, + DMA_TO_DEVICE); + return ret; + } + } + } + + master->cur_msg_mapped = true; + + return 0; +} + +static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg) +{ + struct spi_transfer *xfer; + struct device *tx_dev, *rx_dev; + + if (!master->cur_msg_mapped || !master->can_dma) + return 0; + + tx_dev = &master->dma_tx->dev->device; + rx_dev = &master->dma_rx->dev->device; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (!master->can_dma(master, msg->spi, xfer)) + continue; + + spi_unmap_buf(master, rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE); + spi_unmap_buf(master, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE); + } + + return 0; +} + /* * spi_transfer_one_message - Default implementation of transfer_one_message() * @@ -591,9 +754,9 @@ static int spi_transfer_one_message(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; - bool cur_cs = true; bool keep_cs = false; int ret = 0; + int ms = 1; spi_set_cs(msg->spi, true); @@ -611,7 +774,16 @@ static int spi_transfer_one_message(struct spi_master *master, if (ret > 0) { ret = 0; - wait_for_completion(&master->xfer_completion); + ms = xfer->len * 8 * 1000 / xfer->speed_hz; + ms += 10; /* some tolerance */ + + ms = wait_for_completion_timeout(&master->xfer_completion, + msecs_to_jiffies(ms)); + } + + if (ms == 0) { + dev_err(&msg->spi->dev, "SPI transfer timed out\n"); + msg->status = -ETIMEDOUT; } trace_spi_transfer_stop(msg, xfer); @@ -627,8 +799,9 @@ static int spi_transfer_one_message(struct spi_master *master, &msg->transfers)) { keep_cs = true; } else { - cur_cs = !cur_cs; - spi_set_cs(msg->spi, cur_cs); + spi_set_cs(msg->spi, false); + udelay(10); + spi_set_cs(msg->spi, true); } } @@ -686,6 +859,10 @@ static void spi_pump_messages(struct kthread_work *work) } master->busy = false; spin_unlock_irqrestore(&master->queue_lock, flags); + kfree(master->dummy_rx); + master->dummy_rx = NULL; + kfree(master->dummy_tx); + master->dummy_tx = NULL; if (master->unprepare_transfer_hardware && master->unprepare_transfer_hardware(master)) dev_err(&master->dev, @@ -752,6 +929,13 @@ static void spi_pump_messages(struct kthread_work *work) master->cur_msg_prepared = true; } + ret = spi_map_msg(master, master->cur_msg); + if (ret) { + master->cur_msg->status = ret; + spi_finalize_current_message(master); + return; + } + ret = master->transfer_one_message(master, master->cur_msg); if (ret) { dev_err(&master->dev, @@ -839,6 +1023,8 @@ void spi_finalize_current_message(struct spi_master *master) queue_kthread_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); + spi_unmap_msg(master, mesg); + if (master->cur_msg_prepared && master->unprepare_message) { ret = master->unprepare_message(master, mesg); if (ret) { @@ -892,7 +1078,7 @@ static int spi_stop_queue(struct spi_master *master) */ while ((!list_empty(&master->queue) || master->busy) && limit--) { spin_unlock_irqrestore(&master->queue_lock, flags); - msleep(10); + usleep_range(10000, 11000); spin_lock_irqsave(&master->queue_lock, flags); } @@ -1372,6 +1558,8 @@ int spi_register_master(struct spi_master *master) mutex_init(&master->bus_lock_mutex); master->bus_lock_flag = 0; init_completion(&master->xfer_completion); + if (!master->max_dma_len) + master->max_dma_len = INT_MAX; /* register the device, then userspace will see it. * registration fails if the bus ID is in use. @@ -1597,6 +1785,9 @@ int spi_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; + if (!spi->max_speed_hz) + spi->max_speed_hz = spi->master->max_speed_hz; + if (spi->master->setup) status = spi->master->setup(spi); @@ -1617,11 +1808,10 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; struct spi_transfer *xfer; + int w_size; if (list_empty(&message->transfers)) return -EINVAL; - if (!message->complete) - return -EINVAL; /* Half-duplex links include original MicroWire, and ones with * only one data pin like SPI_3WIRE (switches direction) or where @@ -1652,12 +1842,13 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) message->frame_length += xfer->len; if (!xfer->bits_per_word) xfer->bits_per_word = spi->bits_per_word; - if (!xfer->speed_hz) { + + if (!xfer->speed_hz) xfer->speed_hz = spi->max_speed_hz; - if (master->max_speed_hz && - xfer->speed_hz > master->max_speed_hz) - xfer->speed_hz = master->max_speed_hz; - } + + if (master->max_speed_hz && + xfer->speed_hz > master->max_speed_hz) + xfer->speed_hz = master->max_speed_hz; if (master->bits_per_word_mask) { /* Only 32 bits fit in the mask */ @@ -1668,12 +1859,24 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) return -EINVAL; } + /* + * SPI transfer length should be multiple of SPI word size + * where SPI word size should be power-of-two multiple + */ + if (xfer->bits_per_word <= 8) + w_size = 1; + else if (xfer->bits_per_word <= 16) + w_size = 2; + else + w_size = 4; + + /* No partial transfers accepted */ + if (xfer->len % w_size) + return -EINVAL; + if (xfer->speed_hz && master->min_speed_hz && xfer->speed_hz < master->min_speed_hz) return -EINVAL; - if (xfer->speed_hz && master->max_speed_hz && - xfer->speed_hz > master->max_speed_hz) - return -EINVAL; if (xfer->tx_buf && !xfer->tx_nbits) xfer->tx_nbits = SPI_NBITS_SINGLE; diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index d7c6e36021e8..e3bc23bb5883 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -73,7 +73,8 @@ static DECLARE_BITMAP(minors, N_SPI_MINORS); */ #define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \ | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \ - | SPI_NO_CS | SPI_READY) + | SPI_NO_CS | SPI_READY | SPI_TX_DUAL \ + | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD) struct spidev_data { dev_t devt; @@ -265,6 +266,8 @@ static int spidev_message(struct spidev_data *spidev, buf += k_tmp->len; k_tmp->cs_change = !!u_tmp->cs_change; + k_tmp->tx_nbits = u_tmp->tx_nbits; + k_tmp->rx_nbits = u_tmp->rx_nbits; k_tmp->bits_per_word = u_tmp->bits_per_word; k_tmp->delay_usecs = u_tmp->delay_usecs; k_tmp->speed_hz = u_tmp->speed_hz; @@ -359,6 +362,10 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) retval = __put_user(spi->mode & SPI_MODE_MASK, (__u8 __user *)arg); break; + case SPI_IOC_RD_MODE32: + retval = __put_user(spi->mode & SPI_MODE_MASK, + (__u32 __user *)arg); + break; case SPI_IOC_RD_LSB_FIRST: retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0, (__u8 __user *)arg); @@ -372,9 +379,13 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) /* write requests */ case SPI_IOC_WR_MODE: - retval = __get_user(tmp, (u8 __user *)arg); + case SPI_IOC_WR_MODE32: + if (cmd == SPI_IOC_WR_MODE) + retval = __get_user(tmp, (u8 __user *)arg); + else + retval = __get_user(tmp, (u32 __user *)arg); if (retval == 0) { - u8 save = spi->mode; + u32 save = spi->mode; if (tmp & ~SPI_MODE_MASK) { retval = -EINVAL; @@ -382,18 +393,18 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } tmp |= spi->mode & ~SPI_MODE_MASK; - spi->mode = (u8)tmp; + spi->mode = (u16)tmp; retval = spi_setup(spi); if (retval < 0) spi->mode = save; else - dev_dbg(&spi->dev, "spi mode %02x\n", tmp); + dev_dbg(&spi->dev, "spi mode %x\n", tmp); } break; case SPI_IOC_WR_LSB_FIRST: retval = __get_user(tmp, (__u8 __user *)arg); if (retval == 0) { - u8 save = spi->mode; + u32 save = spi->mode; if (tmp) spi->mode |= SPI_LSB_FIRST; diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig new file mode 100644 index 000000000000..bf1295e19f89 --- /dev/null +++ b/drivers/spmi/Kconfig @@ -0,0 +1,27 @@ +# +# SPMI driver configuration +# +menuconfig SPMI + tristate "SPMI support" + help + SPMI (System Power Management Interface) is a two-wire + serial interface between baseband and application processors + and Power Management Integrated Circuits (PMIC). + +if SPMI + +config SPMI_MSM_PMIC_ARB + tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" + depends on ARM + depends on IRQ_DOMAIN + depends on ARCH_QCOM || COMPILE_TEST + default ARCH_QCOM + help + If you say yes to this option, support will be included for the + built-in SPMI PMIC Arbiter interface on Qualcomm MSM family + processors. + + This is required for communicating with Qualcomm PMICs and + other devices that have the SPMI interface. + +endif diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile new file mode 100644 index 000000000000..fc75104a5aab --- /dev/null +++ b/drivers/spmi/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for kernel SPMI framework. +# +obj-$(CONFIG_SPMI) += spmi.o + +obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c new file mode 100644 index 000000000000..246e03a18c94 --- /dev/null +++ b/drivers/spmi/spmi-pmic-arb.c @@ -0,0 +1,778 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spmi.h> + +/* PMIC Arbiter configuration registers */ +#define PMIC_ARB_VERSION 0x0000 +#define PMIC_ARB_INT_EN 0x0004 + +/* PMIC Arbiter channel registers */ +#define PMIC_ARB_CMD(N) (0x0800 + (0x80 * (N))) +#define PMIC_ARB_CONFIG(N) (0x0804 + (0x80 * (N))) +#define PMIC_ARB_STATUS(N) (0x0808 + (0x80 * (N))) +#define PMIC_ARB_WDATA0(N) (0x0810 + (0x80 * (N))) +#define PMIC_ARB_WDATA1(N) (0x0814 + (0x80 * (N))) +#define PMIC_ARB_RDATA0(N) (0x0818 + (0x80 * (N))) +#define PMIC_ARB_RDATA1(N) (0x081C + (0x80 * (N))) + +/* Interrupt Controller */ +#define SPMI_PIC_OWNER_ACC_STATUS(M, N) (0x0000 + ((32 * (M)) + (4 * (N)))) +#define SPMI_PIC_ACC_ENABLE(N) (0x0200 + (4 * (N))) +#define SPMI_PIC_IRQ_STATUS(N) (0x0600 + (4 * (N))) +#define SPMI_PIC_IRQ_CLEAR(N) (0x0A00 + (4 * (N))) + +/* Mapping Table */ +#define SPMI_MAPPING_TABLE_REG(N) (0x0B00 + (4 * (N))) +#define SPMI_MAPPING_BIT_INDEX(X) (((X) >> 18) & 0xF) +#define SPMI_MAPPING_BIT_IS_0_FLAG(X) (((X) >> 17) & 0x1) +#define SPMI_MAPPING_BIT_IS_0_RESULT(X) (((X) >> 9) & 0xFF) +#define SPMI_MAPPING_BIT_IS_1_FLAG(X) (((X) >> 8) & 0x1) +#define SPMI_MAPPING_BIT_IS_1_RESULT(X) (((X) >> 0) & 0xFF) + +#define SPMI_MAPPING_TABLE_LEN 255 +#define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ + +/* Ownership Table */ +#define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N))) +#define SPMI_OWNERSHIP_PERIPH2OWNER(X) ((X) & 0x7) + +/* Channel Status fields */ +enum pmic_arb_chnl_status { + PMIC_ARB_STATUS_DONE = (1 << 0), + PMIC_ARB_STATUS_FAILURE = (1 << 1), + PMIC_ARB_STATUS_DENIED = (1 << 2), + PMIC_ARB_STATUS_DROPPED = (1 << 3), +}; + +/* Command register fields */ +#define PMIC_ARB_CMD_MAX_BYTE_COUNT 8 + +/* Command Opcodes */ +enum pmic_arb_cmd_op_code { + PMIC_ARB_OP_EXT_WRITEL = 0, + PMIC_ARB_OP_EXT_READL = 1, + PMIC_ARB_OP_EXT_WRITE = 2, + PMIC_ARB_OP_RESET = 3, + PMIC_ARB_OP_SLEEP = 4, + PMIC_ARB_OP_SHUTDOWN = 5, + PMIC_ARB_OP_WAKEUP = 6, + PMIC_ARB_OP_AUTHENTICATE = 7, + PMIC_ARB_OP_MSTR_READ = 8, + PMIC_ARB_OP_MSTR_WRITE = 9, + PMIC_ARB_OP_EXT_READ = 13, + PMIC_ARB_OP_WRITE = 14, + PMIC_ARB_OP_READ = 15, + PMIC_ARB_OP_ZERO_WRITE = 16, +}; + +/* Maximum number of support PMIC peripherals */ +#define PMIC_ARB_MAX_PERIPHS 256 +#define PMIC_ARB_PERIPH_ID_VALID (1 << 15) +#define PMIC_ARB_TIMEOUT_US 100 +#define PMIC_ARB_MAX_TRANS_BYTES (8) + +#define PMIC_ARB_APID_MASK 0xFF +#define PMIC_ARB_PPID_MASK 0xFFF + +/* interrupt enable bit */ +#define SPMI_PIC_ACC_ENABLE_BIT BIT(0) + +/** + * spmi_pmic_arb_dev - SPMI PMIC Arbiter object + * + * @base: address of the PMIC Arbiter core registers. + * @intr: address of the SPMI interrupt control registers. + * @cnfg: address of the PMIC Arbiter configuration registers. + * @lock: lock to synchronize accesses. + * @channel: which channel to use for accesses. + * @irq: PMIC ARB interrupt. + * @ee: the current Execution Environment + * @min_apid: minimum APID (used for bounding IRQ search) + * @max_apid: maximum APID + * @mapping_table: in-memory copy of PPID -> APID mapping table. + * @domain: irq domain object for PMIC IRQ domain + * @spmic: SPMI controller object + * @apid_to_ppid: cached mapping from APID to PPID + */ +struct spmi_pmic_arb_dev { + void __iomem *base; + void __iomem *intr; + void __iomem *cnfg; + raw_spinlock_t lock; + u8 channel; + int irq; + u8 ee; + u8 min_apid; + u8 max_apid; + u32 mapping_table[SPMI_MAPPING_TABLE_LEN]; + struct irq_domain *domain; + struct spmi_controller *spmic; + u16 apid_to_ppid[256]; +}; + +static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset) +{ + return readl_relaxed(dev->base + offset); +} + +static inline void pmic_arb_base_write(struct spmi_pmic_arb_dev *dev, + u32 offset, u32 val) +{ + writel_relaxed(val, dev->base + offset); +} + +/** + * pa_read_data: reads pmic-arb's register and copy 1..4 bytes to buf + * @bc: byte count -1. range: 0..3 + * @reg: register's address + * @buf: output parameter, length must be bc + 1 + */ +static void pa_read_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc) +{ + u32 data = pmic_arb_base_read(dev, reg); + memcpy(buf, &data, (bc & 3) + 1); +} + +/** + * pa_write_data: write 1..4 bytes from buf to pmic-arb's register + * @bc: byte-count -1. range: 0..3. + * @reg: register's address. + * @buf: buffer to write. length must be bc + 1. + */ +static void +pa_write_data(struct spmi_pmic_arb_dev *dev, const u8 *buf, u32 reg, u8 bc) +{ + u32 data = 0; + memcpy(&data, buf, (bc & 3) + 1); + pmic_arb_base_write(dev, reg, data); +} + +static int pmic_arb_wait_for_done(struct spmi_controller *ctrl) +{ + struct spmi_pmic_arb_dev *dev = spmi_controller_get_drvdata(ctrl); + u32 status = 0; + u32 timeout = PMIC_ARB_TIMEOUT_US; + u32 offset = PMIC_ARB_STATUS(dev->channel); + + while (timeout--) { + status = pmic_arb_base_read(dev, offset); + + if (status & PMIC_ARB_STATUS_DONE) { + if (status & PMIC_ARB_STATUS_DENIED) { + dev_err(&ctrl->dev, + "%s: transaction denied (0x%x)\n", + __func__, status); + return -EPERM; + } + + if (status & PMIC_ARB_STATUS_FAILURE) { + dev_err(&ctrl->dev, + "%s: transaction failed (0x%x)\n", + __func__, status); + return -EIO; + } + + if (status & PMIC_ARB_STATUS_DROPPED) { + dev_err(&ctrl->dev, + "%s: transaction dropped (0x%x)\n", + __func__, status); + return -EIO; + } + + return 0; + } + udelay(1); + } + + dev_err(&ctrl->dev, + "%s: timeout, status 0x%x\n", + __func__, status); + return -ETIMEDOUT; +} + +/* Non-data command */ +static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) +{ + struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); + unsigned long flags; + u32 cmd; + int rc; + + /* Check for valid non-data command */ + if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP) + return -EINVAL; + + cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); + + raw_spin_lock_irqsave(&pmic_arb->lock, flags); + pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); + rc = pmic_arb_wait_for_done(ctrl); + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); + + return rc; +} + +static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, + u16 addr, u8 *buf, size_t len) +{ + struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); + unsigned long flags; + u8 bc = len - 1; + u32 cmd; + int rc; + + if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { + dev_err(&ctrl->dev, + "pmic-arb supports 1..%d bytes per trans, but %d requested", + PMIC_ARB_MAX_TRANS_BYTES, len); + return -EINVAL; + } + + /* Check the opcode */ + if (opc >= 0x60 && opc <= 0x7F) + opc = PMIC_ARB_OP_READ; + else if (opc >= 0x20 && opc <= 0x2F) + opc = PMIC_ARB_OP_EXT_READ; + else if (opc >= 0x38 && opc <= 0x3F) + opc = PMIC_ARB_OP_EXT_READL; + else + return -EINVAL; + + cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); + + raw_spin_lock_irqsave(&pmic_arb->lock, flags); + pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); + rc = pmic_arb_wait_for_done(ctrl); + if (rc) + goto done; + + pa_read_data(pmic_arb, buf, PMIC_ARB_RDATA0(pmic_arb->channel), + min_t(u8, bc, 3)); + + if (bc > 3) + pa_read_data(pmic_arb, buf + 4, + PMIC_ARB_RDATA1(pmic_arb->channel), bc - 4); + +done: + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); + return rc; +} + +static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, + u16 addr, const u8 *buf, size_t len) +{ + struct spmi_pmic_arb_dev *pmic_arb = spmi_controller_get_drvdata(ctrl); + unsigned long flags; + u8 bc = len - 1; + u32 cmd; + int rc; + + if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { + dev_err(&ctrl->dev, + "pmic-arb supports 1..%d bytes per trans, but:%d requested", + PMIC_ARB_MAX_TRANS_BYTES, len); + return -EINVAL; + } + + /* Check the opcode */ + if (opc >= 0x40 && opc <= 0x5F) + opc = PMIC_ARB_OP_WRITE; + else if (opc >= 0x00 && opc <= 0x0F) + opc = PMIC_ARB_OP_EXT_WRITE; + else if (opc >= 0x30 && opc <= 0x37) + opc = PMIC_ARB_OP_EXT_WRITEL; + else if (opc >= 0x80 && opc <= 0xFF) + opc = PMIC_ARB_OP_ZERO_WRITE; + else + return -EINVAL; + + cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7); + + /* Write data to FIFOs */ + raw_spin_lock_irqsave(&pmic_arb->lock, flags); + pa_write_data(pmic_arb, buf, PMIC_ARB_WDATA0(pmic_arb->channel) + , min_t(u8, bc, 3)); + if (bc > 3) + pa_write_data(pmic_arb, buf + 4, + PMIC_ARB_WDATA1(pmic_arb->channel), bc - 4); + + /* Start the transaction */ + pmic_arb_base_write(pmic_arb, PMIC_ARB_CMD(pmic_arb->channel), cmd); + rc = pmic_arb_wait_for_done(ctrl); + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); + + return rc; +} + +enum qpnpint_regs { + QPNPINT_REG_RT_STS = 0x10, + QPNPINT_REG_SET_TYPE = 0x11, + QPNPINT_REG_POLARITY_HIGH = 0x12, + QPNPINT_REG_POLARITY_LOW = 0x13, + QPNPINT_REG_LATCHED_CLR = 0x14, + QPNPINT_REG_EN_SET = 0x15, + QPNPINT_REG_EN_CLR = 0x16, + QPNPINT_REG_LATCHED_STS = 0x18, +}; + +struct spmi_pmic_arb_qpnpint_type { + u8 type; /* 1 -> edge */ + u8 polarity_high; + u8 polarity_low; +} __packed; + +/* Simplified accessor functions for irqchip callbacks */ +static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, + size_t len) +{ + struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); + u8 sid = d->hwirq >> 24; + u8 per = d->hwirq >> 16; + + if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, + (per << 8) + reg, buf, len)) + dev_err_ratelimited(&pa->spmic->dev, + "failed irqchip transaction on %x\n", + d->irq); +} + +static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) +{ + struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); + u8 sid = d->hwirq >> 24; + u8 per = d->hwirq >> 16; + + if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid, + (per << 8) + reg, buf, len)) + dev_err_ratelimited(&pa->spmic->dev, + "failed irqchip transaction on %x\n", + d->irq); +} + +static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid) +{ + unsigned int irq; + u32 status; + int id; + + status = readl_relaxed(pa->intr + SPMI_PIC_IRQ_STATUS(apid)); + while (status) { + id = ffs(status) - 1; + status &= ~(1 << id); + irq = irq_find_mapping(pa->domain, + pa->apid_to_ppid[apid] << 16 + | id << 8 + | apid); + generic_handle_irq(irq); + } +} + +static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc) +{ + struct spmi_pmic_arb_dev *pa = irq_get_handler_data(irq); + struct irq_chip *chip = irq_get_chip(irq); + void __iomem *intr = pa->intr; + int first = pa->min_apid >> 5; + int last = pa->max_apid >> 5; + u32 status; + int i, id; + + chained_irq_enter(chip, desc); + + for (i = first; i <= last; ++i) { + status = readl_relaxed(intr + + SPMI_PIC_OWNER_ACC_STATUS(pa->ee, i)); + while (status) { + id = ffs(status) - 1; + status &= ~(1 << id); + periph_interrupt(pa, id + i * 32); + } + } + + chained_irq_exit(chip, desc); +} + +static void qpnpint_irq_ack(struct irq_data *d) +{ + struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); + u8 irq = d->hwirq >> 8; + u8 apid = d->hwirq; + unsigned long flags; + u8 data; + + raw_spin_lock_irqsave(&pa->lock, flags); + writel_relaxed(1 << irq, pa->intr + SPMI_PIC_IRQ_CLEAR(apid)); + raw_spin_unlock_irqrestore(&pa->lock, flags); + + data = 1 << irq; + qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); +} + +static void qpnpint_irq_mask(struct irq_data *d) +{ + struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); + u8 irq = d->hwirq >> 8; + u8 apid = d->hwirq; + unsigned long flags; + u32 status; + u8 data; + + raw_spin_lock_irqsave(&pa->lock, flags); + status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid)); + if (status & SPMI_PIC_ACC_ENABLE_BIT) { + status = status & ~SPMI_PIC_ACC_ENABLE_BIT; + writel_relaxed(status, pa->intr + SPMI_PIC_ACC_ENABLE(apid)); + } + raw_spin_unlock_irqrestore(&pa->lock, flags); + + data = 1 << irq; + qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1); +} + +static void qpnpint_irq_unmask(struct irq_data *d) +{ + struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); + u8 irq = d->hwirq >> 8; + u8 apid = d->hwirq; + unsigned long flags; + u32 status; + u8 data; + + raw_spin_lock_irqsave(&pa->lock, flags); + status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid)); + if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) { + writel_relaxed(status | SPMI_PIC_ACC_ENABLE_BIT, + pa->intr + SPMI_PIC_ACC_ENABLE(apid)); + } + raw_spin_unlock_irqrestore(&pa->lock, flags); + + data = 1 << irq; + qpnpint_spmi_write(d, QPNPINT_REG_EN_SET, &data, 1); +} + +static void qpnpint_irq_enable(struct irq_data *d) +{ + u8 irq = d->hwirq >> 8; + u8 data; + + qpnpint_irq_unmask(d); + + data = 1 << irq; + qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); +} + +static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct spmi_pmic_arb_qpnpint_type type; + u8 irq = d->hwirq >> 8; + + qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); + + if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { + type.type |= 1 << irq; + if (flow_type & IRQF_TRIGGER_RISING) + type.polarity_high |= 1 << irq; + if (flow_type & IRQF_TRIGGER_FALLING) + type.polarity_low |= 1 << irq; + } else { + if ((flow_type & (IRQF_TRIGGER_HIGH)) && + (flow_type & (IRQF_TRIGGER_LOW))) + return -EINVAL; + + type.type &= ~(1 << irq); /* level trig */ + if (flow_type & IRQF_TRIGGER_HIGH) + type.polarity_high |= 1 << irq; + else + type.polarity_low |= 1 << irq; + } + + qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); + return 0; +} + +static struct irq_chip pmic_arb_irqchip = { + .name = "pmic_arb", + .irq_enable = qpnpint_irq_enable, + .irq_ack = qpnpint_irq_ack, + .irq_mask = qpnpint_irq_mask, + .irq_unmask = qpnpint_irq_unmask, + .irq_set_type = qpnpint_irq_set_type, + .flags = IRQCHIP_MASK_ON_SUSPEND + | IRQCHIP_SKIP_SET_WAKE, +}; + +struct spmi_pmic_arb_irq_spec { + unsigned slave:4; + unsigned per:8; + unsigned irq:3; +}; + +static int search_mapping_table(struct spmi_pmic_arb_dev *pa, + struct spmi_pmic_arb_irq_spec *spec, + u8 *apid) +{ + u16 ppid = spec->slave << 8 | spec->per; + u32 *mapping_table = pa->mapping_table; + int index = 0, i; + u32 data; + + for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) { + data = mapping_table[index]; + + if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) { + if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) { + index = SPMI_MAPPING_BIT_IS_1_RESULT(data); + } else { + *apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); + return 0; + } + } else { + if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) { + index = SPMI_MAPPING_BIT_IS_0_RESULT(data); + } else { + *apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); + return 0; + } + } + } + + return -ENODEV; +} + +static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, + struct device_node *controller, + const u32 *intspec, + unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + struct spmi_pmic_arb_dev *pa = d->host_data; + struct spmi_pmic_arb_irq_spec spec; + int err; + u8 apid; + + dev_dbg(&pa->spmic->dev, + "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", + intspec[0], intspec[1], intspec[2]); + + if (d->of_node != controller) + return -EINVAL; + if (intsize != 4) + return -EINVAL; + if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) + return -EINVAL; + + spec.slave = intspec[0]; + spec.per = intspec[1]; + spec.irq = intspec[2]; + + err = search_mapping_table(pa, &spec, &apid); + if (err) + return err; + + pa->apid_to_ppid[apid] = spec.slave << 8 | spec.per; + + /* Keep track of {max,min}_apid for bounding search during interrupt */ + if (apid > pa->max_apid) + pa->max_apid = apid; + if (apid < pa->min_apid) + pa->min_apid = apid; + + *out_hwirq = spec.slave << 24 + | spec.per << 16 + | spec.irq << 8 + | apid; + *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; + + dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); + + return 0; +} + +static int qpnpint_irq_domain_map(struct irq_domain *d, + unsigned int virq, + irq_hw_number_t hwirq) +{ + struct spmi_pmic_arb_dev *pa = d->host_data; + + dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); + + irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); + irq_set_chip_data(virq, d->host_data); + irq_set_noprobe(virq); + return 0; +} + +static const struct irq_domain_ops pmic_arb_irq_domain_ops = { + .map = qpnpint_irq_domain_map, + .xlate = qpnpint_irq_domain_dt_translate, +}; + +static int spmi_pmic_arb_probe(struct platform_device *pdev) +{ + struct spmi_pmic_arb_dev *pa; + struct spmi_controller *ctrl; + struct resource *res; + u32 channel, ee; + int err, i; + + ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); + if (!ctrl) + return -ENOMEM; + + pa = spmi_controller_get_drvdata(ctrl); + pa->spmic = ctrl; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); + pa->base = devm_ioremap_resource(&ctrl->dev, res); + if (IS_ERR(pa->base)) { + err = PTR_ERR(pa->base); + goto err_put_ctrl; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); + pa->intr = devm_ioremap_resource(&ctrl->dev, res); + if (IS_ERR(pa->intr)) { + err = PTR_ERR(pa->intr); + goto err_put_ctrl; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg"); + pa->cnfg = devm_ioremap_resource(&ctrl->dev, res); + if (IS_ERR(pa->cnfg)) { + err = PTR_ERR(pa->cnfg); + goto err_put_ctrl; + } + + pa->irq = platform_get_irq_byname(pdev, "periph_irq"); + if (pa->irq < 0) { + err = pa->irq; + goto err_put_ctrl; + } + + err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); + if (err) { + dev_err(&pdev->dev, "channel unspecified.\n"); + goto err_put_ctrl; + } + + if (channel > 5) { + dev_err(&pdev->dev, "invalid channel (%u) specified.\n", + channel); + goto err_put_ctrl; + } + + pa->channel = channel; + + err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee); + if (err) { + dev_err(&pdev->dev, "EE unspecified.\n"); + goto err_put_ctrl; + } + + if (ee > 5) { + dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee); + err = -EINVAL; + goto err_put_ctrl; + } + + pa->ee = ee; + + for (i = 0; i < ARRAY_SIZE(pa->mapping_table); ++i) + pa->mapping_table[i] = readl_relaxed( + pa->cnfg + SPMI_MAPPING_TABLE_REG(i)); + + /* Initialize max_apid/min_apid to the opposite bounds, during + * the irq domain translation, we are sure to update these */ + pa->max_apid = 0; + pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1; + + platform_set_drvdata(pdev, ctrl); + raw_spin_lock_init(&pa->lock); + + ctrl->cmd = pmic_arb_cmd; + ctrl->read_cmd = pmic_arb_read_cmd; + ctrl->write_cmd = pmic_arb_write_cmd; + + dev_dbg(&pdev->dev, "adding irq domain\n"); + pa->domain = irq_domain_add_tree(pdev->dev.of_node, + &pmic_arb_irq_domain_ops, pa); + if (!pa->domain) { + dev_err(&pdev->dev, "unable to create irq_domain\n"); + err = -ENOMEM; + goto err_put_ctrl; + } + + irq_set_handler_data(pa->irq, pa); + irq_set_chained_handler(pa->irq, pmic_arb_chained_irq); + + err = spmi_controller_add(ctrl); + if (err) + goto err_domain_remove; + + dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n", + pmic_arb_base_read(pa, PMIC_ARB_VERSION)); + + return 0; + +err_domain_remove: + irq_set_chained_handler(pa->irq, NULL); + irq_set_handler_data(pa->irq, NULL); + irq_domain_remove(pa->domain); +err_put_ctrl: + spmi_controller_put(ctrl); + return err; +} + +static int spmi_pmic_arb_remove(struct platform_device *pdev) +{ + struct spmi_controller *ctrl = platform_get_drvdata(pdev); + struct spmi_pmic_arb_dev *pa = spmi_controller_get_drvdata(ctrl); + spmi_controller_remove(ctrl); + irq_set_chained_handler(pa->irq, NULL); + irq_set_handler_data(pa->irq, NULL); + irq_domain_remove(pa->domain); + spmi_controller_put(ctrl); + return 0; +} + +static const struct of_device_id spmi_pmic_arb_match_table[] = { + { .compatible = "qcom,spmi-pmic-arb", }, + {}, +}; +MODULE_DEVICE_TABLE(of, spmi_pmic_arb_match_table); + +static struct platform_driver spmi_pmic_arb_driver = { + .probe = spmi_pmic_arb_probe, + .remove = spmi_pmic_arb_remove, + .driver = { + .name = "spmi_pmic_arb", + .owner = THIS_MODULE, + .of_match_table = spmi_pmic_arb_match_table, + }, +}; +module_platform_driver(spmi_pmic_arb_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:spmi_pmic_arb"); diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c new file mode 100644 index 000000000000..3b5780710d50 --- /dev/null +++ b/drivers/spmi/spmi.c @@ -0,0 +1,574 @@ +/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/idr.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/spmi.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> + +#include <dt-bindings/spmi/spmi.h> + +static DEFINE_IDA(ctrl_ida); + +static void spmi_dev_release(struct device *dev) +{ + struct spmi_device *sdev = to_spmi_device(dev); + kfree(sdev); +} + +static const struct device_type spmi_dev_type = { + .release = spmi_dev_release, +}; + +static void spmi_ctrl_release(struct device *dev) +{ + struct spmi_controller *ctrl = to_spmi_controller(dev); + ida_simple_remove(&ctrl_ida, ctrl->nr); + kfree(ctrl); +} + +static const struct device_type spmi_ctrl_type = { + .release = spmi_ctrl_release, +}; + +static int spmi_device_match(struct device *dev, struct device_driver *drv) +{ + if (of_driver_match_device(dev, drv)) + return 1; + + if (drv->name) + return strncmp(dev_name(dev), drv->name, + SPMI_NAME_SIZE) == 0; + + return 0; +} + +/** + * spmi_device_add() - add a device previously constructed via spmi_device_alloc() + * @sdev: spmi_device to be added + */ +int spmi_device_add(struct spmi_device *sdev) +{ + struct spmi_controller *ctrl = sdev->ctrl; + int err; + + dev_set_name(&sdev->dev, "%d-%02x", ctrl->nr, sdev->usid); + + err = device_add(&sdev->dev); + if (err < 0) { + dev_err(&sdev->dev, "Can't add %s, status %d\n", + dev_name(&sdev->dev), err); + goto err_device_add; + } + + dev_dbg(&sdev->dev, "device %s registered\n", dev_name(&sdev->dev)); + +err_device_add: + return err; +} +EXPORT_SYMBOL_GPL(spmi_device_add); + +/** + * spmi_device_remove(): remove an SPMI device + * @sdev: spmi_device to be removed + */ +void spmi_device_remove(struct spmi_device *sdev) +{ + device_unregister(&sdev->dev); +} +EXPORT_SYMBOL_GPL(spmi_device_remove); + +static inline int +spmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid) +{ + if (!ctrl || !ctrl->cmd || ctrl->dev.type != &spmi_ctrl_type) + return -EINVAL; + + return ctrl->cmd(ctrl, opcode, sid); +} + +static inline int spmi_read_cmd(struct spmi_controller *ctrl, u8 opcode, + u8 sid, u16 addr, u8 *buf, size_t len) +{ + if (!ctrl || !ctrl->read_cmd || ctrl->dev.type != &spmi_ctrl_type) + return -EINVAL; + + return ctrl->read_cmd(ctrl, opcode, sid, addr, buf, len); +} + +static inline int spmi_write_cmd(struct spmi_controller *ctrl, u8 opcode, + u8 sid, u16 addr, const u8 *buf, size_t len) +{ + if (!ctrl || !ctrl->write_cmd || ctrl->dev.type != &spmi_ctrl_type) + return -EINVAL; + + return ctrl->write_cmd(ctrl, opcode, sid, addr, buf, len); +} + +/** + * spmi_register_read() - register read + * @sdev: SPMI device. + * @addr: slave register address (5-bit address). + * @buf: buffer to be populated with data from the Slave. + * + * Reads 1 byte of data from a Slave device register. + */ +int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf) +{ + /* 5-bit register address */ + if (addr > 0x1F) + return -EINVAL; + + return spmi_read_cmd(sdev->ctrl, SPMI_CMD_READ, sdev->usid, addr, + buf, 1); +} +EXPORT_SYMBOL_GPL(spmi_register_read); + +/** + * spmi_ext_register_read() - extended register read + * @sdev: SPMI device. + * @addr: slave register address (8-bit address). + * @buf: buffer to be populated with data from the Slave. + * @len: the request number of bytes to read (up to 16 bytes). + * + * Reads up to 16 bytes of data from the extended register space on a + * Slave device. + */ +int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf, + size_t len) +{ + /* 8-bit register address, up to 16 bytes */ + if (len == 0 || len > 16) + return -EINVAL; + + return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READ, sdev->usid, addr, + buf, len); +} +EXPORT_SYMBOL_GPL(spmi_ext_register_read); + +/** + * spmi_ext_register_readl() - extended register read long + * @sdev: SPMI device. + * @addr: slave register address (16-bit address). + * @buf: buffer to be populated with data from the Slave. + * @len: the request number of bytes to read (up to 8 bytes). + * + * Reads up to 8 bytes of data from the extended register space on a + * Slave device using 16-bit address. + */ +int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf, + size_t len) +{ + /* 16-bit register address, up to 8 bytes */ + if (len == 0 || len > 8) + return -EINVAL; + + return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READL, sdev->usid, addr, + buf, len); +} +EXPORT_SYMBOL_GPL(spmi_ext_register_readl); + +/** + * spmi_register_write() - register write + * @sdev: SPMI device + * @addr: slave register address (5-bit address). + * @data: buffer containing the data to be transferred to the Slave. + * + * Writes 1 byte of data to a Slave device register. + */ +int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data) +{ + /* 5-bit register address */ + if (addr > 0x1F) + return -EINVAL; + + return spmi_write_cmd(sdev->ctrl, SPMI_CMD_WRITE, sdev->usid, addr, + &data, 1); +} +EXPORT_SYMBOL_GPL(spmi_register_write); + +/** + * spmi_register_zero_write() - register zero write + * @sdev: SPMI device. + * @data: the data to be written to register 0 (7-bits). + * + * Writes data to register 0 of the Slave device. + */ +int spmi_register_zero_write(struct spmi_device *sdev, u8 data) +{ + return spmi_write_cmd(sdev->ctrl, SPMI_CMD_ZERO_WRITE, sdev->usid, 0, + &data, 1); +} +EXPORT_SYMBOL_GPL(spmi_register_zero_write); + +/** + * spmi_ext_register_write() - extended register write + * @sdev: SPMI device. + * @addr: slave register address (8-bit address). + * @buf: buffer containing the data to be transferred to the Slave. + * @len: the request number of bytes to read (up to 16 bytes). + * + * Writes up to 16 bytes of data to the extended register space of a + * Slave device. + */ +int spmi_ext_register_write(struct spmi_device *sdev, u8 addr, const u8 *buf, + size_t len) +{ + /* 8-bit register address, up to 16 bytes */ + if (len == 0 || len > 16) + return -EINVAL; + + return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITE, sdev->usid, addr, + buf, len); +} +EXPORT_SYMBOL_GPL(spmi_ext_register_write); + +/** + * spmi_ext_register_writel() - extended register write long + * @sdev: SPMI device. + * @addr: slave register address (16-bit address). + * @buf: buffer containing the data to be transferred to the Slave. + * @len: the request number of bytes to read (up to 8 bytes). + * + * Writes up to 8 bytes of data to the extended register space of a + * Slave device using 16-bit address. + */ +int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, const u8 *buf, + size_t len) +{ + /* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */ + if (len == 0 || len > 8) + return -EINVAL; + + return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITEL, sdev->usid, + addr, buf, len); +} +EXPORT_SYMBOL_GPL(spmi_ext_register_writel); + +/** + * spmi_command_reset() - sends RESET command to the specified slave + * @sdev: SPMI device. + * + * The Reset command initializes the Slave and forces all registers to + * their reset values. The Slave shall enter the STARTUP state after + * receiving a Reset command. + */ +int spmi_command_reset(struct spmi_device *sdev) +{ + return spmi_cmd(sdev->ctrl, SPMI_CMD_RESET, sdev->usid); +} +EXPORT_SYMBOL_GPL(spmi_command_reset); + +/** + * spmi_command_sleep() - sends SLEEP command to the specified SPMI device + * @sdev: SPMI device. + * + * The Sleep command causes the Slave to enter the user defined SLEEP state. + */ +int spmi_command_sleep(struct spmi_device *sdev) +{ + return spmi_cmd(sdev->ctrl, SPMI_CMD_SLEEP, sdev->usid); +} +EXPORT_SYMBOL_GPL(spmi_command_sleep); + +/** + * spmi_command_wakeup() - sends WAKEUP command to the specified SPMI device + * @sdev: SPMI device. + * + * The Wakeup command causes the Slave to move from the SLEEP state to + * the ACTIVE state. + */ +int spmi_command_wakeup(struct spmi_device *sdev) +{ + return spmi_cmd(sdev->ctrl, SPMI_CMD_WAKEUP, sdev->usid); +} +EXPORT_SYMBOL_GPL(spmi_command_wakeup); + +/** + * spmi_command_shutdown() - sends SHUTDOWN command to the specified SPMI device + * @sdev: SPMI device. + * + * The Shutdown command causes the Slave to enter the SHUTDOWN state. + */ +int spmi_command_shutdown(struct spmi_device *sdev) +{ + return spmi_cmd(sdev->ctrl, SPMI_CMD_SHUTDOWN, sdev->usid); +} +EXPORT_SYMBOL_GPL(spmi_command_shutdown); + +static int spmi_drv_probe(struct device *dev) +{ + const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); + struct spmi_device *sdev = to_spmi_device(dev); + int err; + + /* Ensure the slave is in ACTIVE state */ + err = spmi_command_wakeup(sdev); + if (err) + goto fail_wakeup; + + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + err = sdrv->probe(sdev); + if (err) + goto fail_probe; + + return 0; + +fail_probe: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); +fail_wakeup: + return err; +} + +static int spmi_drv_remove(struct device *dev) +{ + const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); + + pm_runtime_get_sync(dev); + sdrv->remove(to_spmi_device(dev)); + pm_runtime_put_noidle(dev); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + return 0; +} + +static struct bus_type spmi_bus_type = { + .name = "spmi", + .match = spmi_device_match, + .probe = spmi_drv_probe, + .remove = spmi_drv_remove, +}; + +/** + * spmi_controller_alloc() - Allocate a new SPMI device + * @ctrl: associated controller + * + * Caller is responsible for either calling spmi_device_add() to add the + * newly allocated controller, or calling spmi_device_put() to discard it. + */ +struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl) +{ + struct spmi_device *sdev; + + sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); + if (!sdev) + return NULL; + + sdev->ctrl = ctrl; + device_initialize(&sdev->dev); + sdev->dev.parent = &ctrl->dev; + sdev->dev.bus = &spmi_bus_type; + sdev->dev.type = &spmi_dev_type; + return sdev; +} +EXPORT_SYMBOL_GPL(spmi_device_alloc); + +/** + * spmi_controller_alloc() - Allocate a new SPMI controller + * @parent: parent device + * @size: size of private data + * + * Caller is responsible for either calling spmi_controller_add() to add the + * newly allocated controller, or calling spmi_controller_put() to discard it. + * The allocated private data region may be accessed via + * spmi_controller_get_drvdata() + */ +struct spmi_controller *spmi_controller_alloc(struct device *parent, + size_t size) +{ + struct spmi_controller *ctrl; + int id; + + if (WARN_ON(!parent)) + return NULL; + + ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); + if (!ctrl) + return NULL; + + device_initialize(&ctrl->dev); + ctrl->dev.type = &spmi_ctrl_type; + ctrl->dev.bus = &spmi_bus_type; + ctrl->dev.parent = parent; + ctrl->dev.of_node = parent->of_node; + spmi_controller_set_drvdata(ctrl, &ctrl[1]); + + id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + dev_err(parent, + "unable to allocate SPMI controller identifier.\n"); + spmi_controller_put(ctrl); + return NULL; + } + + ctrl->nr = id; + dev_set_name(&ctrl->dev, "spmi-%d", id); + + dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id); + return ctrl; +} +EXPORT_SYMBOL_GPL(spmi_controller_alloc); + +static void of_spmi_register_devices(struct spmi_controller *ctrl) +{ + struct device_node *node; + int err; + + if (!ctrl->dev.of_node) + return; + + for_each_available_child_of_node(ctrl->dev.of_node, node) { + struct spmi_device *sdev; + u32 reg[2]; + + dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name); + + err = of_property_read_u32_array(node, "reg", reg, 2); + if (err) { + dev_err(&ctrl->dev, + "node %s err (%d) does not have 'reg' property\n", + node->full_name, err); + continue; + } + + if (reg[1] != SPMI_USID) { + dev_err(&ctrl->dev, + "node %s contains unsupported 'reg' entry\n", + node->full_name); + continue; + } + + if (reg[0] >= SPMI_MAX_SLAVE_ID) { + dev_err(&ctrl->dev, + "invalid usid on node %s\n", + node->full_name); + continue; + } + + dev_dbg(&ctrl->dev, "read usid %02x\n", reg[0]); + + sdev = spmi_device_alloc(ctrl); + if (!sdev) + continue; + + sdev->dev.of_node = node; + sdev->usid = (u8) reg[0]; + + err = spmi_device_add(sdev); + if (err) { + dev_err(&sdev->dev, + "failure adding device. status %d\n", err); + spmi_device_put(sdev); + } + } +} + +/** + * spmi_controller_add() - Add an SPMI controller + * @ctrl: controller to be registered. + * + * Register a controller previously allocated via spmi_controller_alloc() with + * the SPMI core. + */ +int spmi_controller_add(struct spmi_controller *ctrl) +{ + int ret; + + /* Can't register until after driver model init */ + if (WARN_ON(!spmi_bus_type.p)) + return -EAGAIN; + + ret = device_add(&ctrl->dev); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_OF)) + of_spmi_register_devices(ctrl); + + dev_dbg(&ctrl->dev, "spmi-%d registered: dev:%p\n", + ctrl->nr, &ctrl->dev); + + return 0; +}; +EXPORT_SYMBOL_GPL(spmi_controller_add); + +/* Remove a device associated with a controller */ +static int spmi_ctrl_remove_device(struct device *dev, void *data) +{ + struct spmi_device *spmidev = to_spmi_device(dev); + if (dev->type == &spmi_dev_type) + spmi_device_remove(spmidev); + return 0; +} + +/** + * spmi_controller_remove(): remove an SPMI controller + * @ctrl: controller to remove + * + * Remove a SPMI controller. Caller is responsible for calling + * spmi_controller_put() to discard the allocated controller. + */ +void spmi_controller_remove(struct spmi_controller *ctrl) +{ + int dummy; + + if (!ctrl) + return; + + dummy = device_for_each_child(&ctrl->dev, NULL, + spmi_ctrl_remove_device); + device_del(&ctrl->dev); +} +EXPORT_SYMBOL_GPL(spmi_controller_remove); + +/** + * spmi_driver_register() - Register client driver with SPMI core + * @sdrv: client driver to be associated with client-device. + * + * This API will register the client driver with the SPMI framework. + * It is typically called from the driver's module-init function. + */ +int spmi_driver_register(struct spmi_driver *sdrv) +{ + sdrv->driver.bus = &spmi_bus_type; + return driver_register(&sdrv->driver); +} +EXPORT_SYMBOL_GPL(spmi_driver_register); + +static void __exit spmi_exit(void) +{ + bus_unregister(&spmi_bus_type); +} +module_exit(spmi_exit); + +static int __init spmi_init(void) +{ + return bus_register(&spmi_bus_type); +} +postcore_initcall(spmi_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SPMI module"); +MODULE_ALIAS("platform:spmi"); diff --git a/drivers/staging/cxt1e1/linux.c b/drivers/staging/cxt1e1/linux.c index 4a08e16e42f7..79206cb3fb94 100644 --- a/drivers/staging/cxt1e1/linux.c +++ b/drivers/staging/cxt1e1/linux.c @@ -866,6 +866,8 @@ c4_ioctl (struct net_device *ndev, struct ifreq *ifr, int cmd) _IOC_SIZE (iocmd)); #endif iolen = _IOC_SIZE (iocmd); + if (iolen > sizeof(arg)) + return -EFAULT; data = ifr->ifr_data + sizeof (iocmd); if (copy_from_user (&arg, data, iolen)) return -EFAULT; diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index 8af136e9c9dc..b22142ee5262 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -2036,6 +2036,13 @@ static void fwserial_auto_connect(struct work_struct *work) schedule_delayed_work(&peer->connect, CONNECT_RETRY_DELAY); } +static void fwserial_peer_workfn(struct work_struct *work) +{ + struct fwtty_peer *peer = to_peer(work, work); + + peer->workfn(work); +} + /** * fwserial_add_peer - add a newly probed 'serial' unit device as a 'peer' * @serial: aggregate representing the specific fw_card to add the peer to @@ -2100,7 +2107,7 @@ static int fwserial_add_peer(struct fw_serial *serial, struct fw_unit *unit) peer->port = NULL; init_timer(&peer->timer); - INIT_WORK(&peer->work, NULL); + INIT_WORK(&peer->work, fwserial_peer_workfn); INIT_DELAYED_WORK(&peer->connect, fwserial_auto_connect); /* associate peer with specific fw_card */ @@ -2702,7 +2709,7 @@ static int fwserial_parse_mgmt_write(struct fwtty_peer *peer, } else { peer->work_params.plug_req = pkt->plug_req; - PREPARE_WORK(&peer->work, fwserial_handle_plug_req); + peer->workfn = fwserial_handle_plug_req; queue_work(system_unbound_wq, &peer->work); } break; @@ -2731,7 +2738,7 @@ static int fwserial_parse_mgmt_write(struct fwtty_peer *peer, fwtty_err(&peer->unit, "unplug req: busy\n"); rcode = RCODE_CONFLICT_ERROR; } else { - PREPARE_WORK(&peer->work, fwserial_handle_unplug_req); + peer->workfn = fwserial_handle_unplug_req; queue_work(system_unbound_wq, &peer->work); } break; diff --git a/drivers/staging/fwserial/fwserial.h b/drivers/staging/fwserial/fwserial.h index 54f7f9b9b212..98b853d4acbc 100644 --- a/drivers/staging/fwserial/fwserial.h +++ b/drivers/staging/fwserial/fwserial.h @@ -91,6 +91,7 @@ struct fwtty_peer { struct rcu_head rcu; spinlock_t lock; + work_func_t workfn; struct work_struct work; struct peer_work_params work_params; struct timer_list timer; diff --git a/drivers/staging/line6/audio.c b/drivers/staging/line6/audio.c index a92e21f7d55b..171d80c1b020 100644 --- a/drivers/staging/line6/audio.c +++ b/drivers/staging/line6/audio.c @@ -24,8 +24,9 @@ int line6_init_audio(struct usb_line6 *line6) struct snd_card *card; int err; - err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); + err = snd_card_new(line6->ifcdev, + SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); if (err < 0) return err; diff --git a/drivers/staging/line6/midi.c b/drivers/staging/line6/midi.c index 3f6d78c585fb..c61cd0515baf 100644 --- a/drivers/staging/line6/midi.c +++ b/drivers/staging/line6/midi.c @@ -307,8 +307,6 @@ int line6_init_midi(struct usb_line6 *line6) if (err < 0) return err; - snd_card_set_dev(line6->card, line6->ifcdev); - err = snd_line6_new_midi(line6midi); if (err < 0) return err; diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c index df8331bce175..661080b3c39d 100644 --- a/drivers/staging/line6/pcm.c +++ b/drivers/staging/line6/pcm.c @@ -501,8 +501,6 @@ int line6_init_pcm(struct usb_line6 *line6, if (err < 0) return err; - snd_card_set_dev(line6->card, line6->ifcdev); - err = snd_line6_new_pcm(line6pcm); if (err < 0) return err; diff --git a/drivers/staging/media/go7007/snd-go7007.c b/drivers/staging/media/go7007/snd-go7007.c index 16dd64920767..9eb2a20ae40a 100644 --- a/drivers/staging/media/go7007/snd-go7007.c +++ b/drivers/staging/media/go7007/snd-go7007.c @@ -245,8 +245,8 @@ int go7007_snd_init(struct go7007 *go) spin_lock_init(&gosnd->lock); gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; gosnd->capturing = 0; - ret = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, - &gosnd->card); + ret = snd_card_new(go->dev, index[dev], id[dev], THIS_MODULE, 0, + &gosnd->card); if (ret < 0) { kfree(gosnd); return ret; @@ -257,7 +257,6 @@ int go7007_snd_init(struct go7007 *go) kfree(gosnd); return ret; } - snd_card_set_dev(gosnd->card, go->dev); ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm); if (ret < 0) { snd_card_free(gosnd->card); diff --git a/drivers/staging/media/solo6x10/solo6x10-g723.c b/drivers/staging/media/solo6x10/solo6x10-g723.c index 1db18c7972a0..74f037b6166c 100644 --- a/drivers/staging/media/solo6x10/solo6x10-g723.c +++ b/drivers/staging/media/solo6x10/solo6x10-g723.c @@ -366,8 +366,9 @@ int solo_g723_init(struct solo_dev *solo_dev) /* Allows for easier mapping between video and audio */ sprintf(name, "Softlogic%d", solo_dev->vfd->num); - ret = snd_card_create(SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, - &solo_dev->snd_card); + ret = snd_card_new(&solo_dev->pdev->dev, + SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, + &solo_dev->snd_card); if (ret < 0) return ret; @@ -377,7 +378,6 @@ int solo_g723_init(struct solo_dev *solo_dev) strcpy(card->shortname, "SOLO-6x10 Audio"); sprintf(card->longname, "%s on %s IRQ %d", card->shortname, pci_name(solo_dev->pdev), solo_dev->pdev->irq); - snd_card_set_dev(card, &solo_dev->pdev->dev); ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops); if (ret < 0) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 7f1a7ce4b771..b83ec378d04f 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -785,7 +785,7 @@ static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn) spin_unlock_bh(&conn->cmd_lock); list_for_each_entry_safe(cmd, cmd_p, &ack_list, i_conn_node) { - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); iscsit_free_cmd(cmd, false); } } @@ -3708,7 +3708,7 @@ iscsit_immediate_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state break; case ISTATE_REMOVE: spin_lock_bh(&conn->cmd_lock); - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); iscsit_free_cmd(cmd, false); @@ -4151,7 +4151,7 @@ static void iscsit_release_commands_from_conn(struct iscsi_conn *conn) spin_lock_bh(&conn->cmd_lock); list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) { - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); iscsit_increment_maxcmdsn(cmd, sess); @@ -4196,6 +4196,10 @@ int iscsit_close_connection( iscsit_stop_timers_for_cmds(conn); iscsit_stop_nopin_response_timer(conn); iscsit_stop_nopin_timer(conn); + + if (conn->conn_transport->iscsit_wait_conn) + conn->conn_transport->iscsit_wait_conn(conn); + iscsit_free_queue_reqs_for_conn(conn); /* diff --git a/drivers/target/iscsi/iscsi_target_erl2.c b/drivers/target/iscsi/iscsi_target_erl2.c index 33be1fb1df32..4ca8fd2a70db 100644 --- a/drivers/target/iscsi/iscsi_target_erl2.c +++ b/drivers/target/iscsi/iscsi_target_erl2.c @@ -138,7 +138,7 @@ void iscsit_free_connection_recovery_entires(struct iscsi_session *sess) list_for_each_entry_safe(cmd, cmd_tmp, &cr->conn_recovery_cmd_list, i_conn_node) { - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); cmd->conn = NULL; spin_unlock(&cr->conn_recovery_cmd_lock); iscsit_free_cmd(cmd, true); @@ -160,7 +160,7 @@ void iscsit_free_connection_recovery_entires(struct iscsi_session *sess) list_for_each_entry_safe(cmd, cmd_tmp, &cr->conn_recovery_cmd_list, i_conn_node) { - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); cmd->conn = NULL; spin_unlock(&cr->conn_recovery_cmd_lock); iscsit_free_cmd(cmd, true); @@ -216,7 +216,7 @@ int iscsit_remove_cmd_from_connection_recovery( } cr = cmd->cr; - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); return --cr->cmd_count; } @@ -297,7 +297,7 @@ int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *conn) if (!(cmd->cmd_flags & ICF_OOO_CMDSN)) continue; - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); iscsit_free_cmd(cmd, true); @@ -335,7 +335,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) /* * Only perform connection recovery on ISCSI_OP_SCSI_CMD or * ISCSI_OP_NOOP_OUT opcodes. For all other opcodes call - * list_del(&cmd->i_conn_node); to release the command to the + * list_del_init(&cmd->i_conn_node); to release the command to the * session pool and remove it from the connection's list. * * Also stop the DataOUT timer, which will be restarted after @@ -351,7 +351,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) " CID: %hu\n", cmd->iscsi_opcode, cmd->init_task_tag, cmd->cmd_sn, conn->cid); - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); iscsit_free_cmd(cmd, true); spin_lock_bh(&conn->cmd_lock); @@ -371,7 +371,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) */ if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd && iscsi_sna_gte(cmd->cmd_sn, conn->sess->exp_cmd_sn)) { - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); iscsit_free_cmd(cmd, true); spin_lock_bh(&conn->cmd_lock); @@ -393,7 +393,7 @@ int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) cmd->sess = conn->sess; - list_del(&cmd->i_conn_node); + list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); iscsit_free_all_datain_reqs(cmd); diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 39761837608d..44a5471de00f 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -137,7 +137,7 @@ struct iscsi_portal_group *iscsit_get_tpg_from_np( list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) { spin_lock(&tpg->tpg_state_lock); - if (tpg->tpg_state == TPG_STATE_FREE) { + if (tpg->tpg_state != TPG_STATE_ACTIVE) { spin_unlock(&tpg->tpg_state_lock); continue; } diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 42f18fc1067b..77e6531fb0a1 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -1079,25 +1079,31 @@ sbc_dif_copy_prot(struct se_cmd *cmd, unsigned int sectors, bool read, left = sectors * dev->prot_length; for_each_sg(cmd->t_prot_sg, psg, cmd->t_prot_nents, i) { - - len = min(psg->length, left); - if (offset >= sg->length) { - sg = sg_next(sg); - offset = 0; - } + unsigned int psg_len, copied = 0; paddr = kmap_atomic(sg_page(psg)) + psg->offset; - addr = kmap_atomic(sg_page(sg)) + sg->offset + offset; - - if (read) - memcpy(paddr, addr, len); - else - memcpy(addr, paddr, len); - - left -= len; - offset += len; + psg_len = min(left, psg->length); + while (psg_len) { + len = min(psg_len, sg->length - offset); + addr = kmap_atomic(sg_page(sg)) + sg->offset + offset; + + if (read) + memcpy(paddr + copied, addr, len); + else + memcpy(addr, paddr + copied, len); + + left -= len; + offset += len; + copied += len; + psg_len -= len; + + if (offset >= sg->length) { + sg = sg_next(sg); + offset = 0; + } + kunmap_atomic(addr); + } kunmap_atomic(paddr); - kunmap_atomic(addr); } } diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 35c066489a19..5f88d767671e 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -136,6 +136,7 @@ config SPEAR_THERMAL config RCAR_THERMAL tristate "Renesas R-Car thermal driver" depends on ARCH_SHMOBILE || COMPILE_TEST + depends on HAS_IOMEM help Enable this to plug the R-Car thermal sensor driver into the Linux thermal framework. @@ -210,8 +211,16 @@ config ACPI_INT3403_THERMAL tristate "ACPI INT3403 thermal driver" depends on X86 && ACPI help - This driver uses ACPI INT3403 device objects. If present, it will - register each INT3403 thermal sensor as a thermal zone. + Newer laptops and tablets that use ACPI may have thermal sensors + outside the core CPU/SOC for thermal safety reasons. These + temperature sensors are also exposed for the OS to use via the so + called INT3403 ACPI object. This driver will, on devices that have + such sensors, expose the temperature information from these sensors + to userspace via the normal thermal framework. This means that a wide + range of applications and GUI widgets can show this information to + the user or use this information for making decisions. For example, + the Intel Thermal Daemon can use this information to allow the user + to select his laptop to run without turning on the fans. menu "Texas Instruments thermal drivers" source "drivers/thermal/ti-soc-thermal/Kconfig" diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 338a88bf6662..71b0ec0c370d 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -56,10 +56,15 @@ static LIST_HEAD(thermal_governor_list); static DEFINE_MUTEX(thermal_list_lock); static DEFINE_MUTEX(thermal_governor_lock); +static struct thermal_governor *def_governor; + static struct thermal_governor *__find_governor(const char *name) { struct thermal_governor *pos; + if (!name || !name[0]) + return def_governor; + list_for_each_entry(pos, &thermal_governor_list, governor_list) if (!strnicmp(name, pos->name, THERMAL_NAME_LENGTH)) return pos; @@ -82,17 +87,23 @@ int thermal_register_governor(struct thermal_governor *governor) if (__find_governor(governor->name) == NULL) { err = 0; list_add(&governor->governor_list, &thermal_governor_list); + if (!def_governor && !strncmp(governor->name, + DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH)) + def_governor = governor; } mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) { + /* + * only thermal zones with specified tz->tzp->governor_name + * may run with tz->govenor unset + */ if (pos->governor) continue; - if (pos->tzp) - name = pos->tzp->governor_name; - else - name = DEFAULT_THERMAL_GOVERNOR; + + name = pos->tzp->governor_name; + if (!strnicmp(name, governor->name, THERMAL_NAME_LENGTH)) pos->governor = governor; } @@ -342,8 +353,8 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz) static void handle_non_critical_trips(struct thermal_zone_device *tz, int trip, enum thermal_trip_type trip_type) { - if (tz->governor) - tz->governor->throttle(tz, trip); + tz->governor ? tz->governor->throttle(tz, trip) : + def_governor->throttle(tz, trip); } static void handle_critical_trips(struct thermal_zone_device *tz, @@ -1107,7 +1118,7 @@ __thermal_cooling_device_register(struct device_node *np, INIT_LIST_HEAD(&cdev->thermal_instances); cdev->np = np; cdev->ops = ops; - cdev->updated = true; + cdev->updated = false; cdev->device.class = &thermal_class; cdev->devdata = devdata; dev_set_name(&cdev->device, "cooling_device%d", cdev->id); @@ -1533,7 +1544,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, if (tz->tzp) tz->governor = __find_governor(tz->tzp->governor_name); else - tz->governor = __find_governor(DEFAULT_THERMAL_GOVERNOR); + tz->governor = def_governor; mutex_unlock(&thermal_governor_lock); diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 972e1c73722a..081fd7e6a9f0 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -68,6 +68,10 @@ struct phy_dev_entry { struct thermal_zone_device *tzone; }; +static const struct thermal_zone_params pkg_temp_tz_params = { + .no_hwmon = true, +}; + /* List maintaining number of package instances */ static LIST_HEAD(phy_dev_list); static DEFINE_MUTEX(phy_dev_list_mutex); @@ -394,7 +398,6 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) int err; u32 tj_max; struct phy_dev_entry *phy_dev_entry; - char buffer[30]; int thres_count; u32 eax, ebx, ecx, edx; u8 *temp; @@ -440,13 +443,11 @@ static int pkg_temp_thermal_device_add(unsigned int cpu) phy_dev_entry->first_cpu = cpu; phy_dev_entry->tj_max = tj_max; phy_dev_entry->ref_cnt = 1; - snprintf(buffer, sizeof(buffer), "pkg-temp-%d\n", - phy_dev_entry->phys_proc_id); - phy_dev_entry->tzone = thermal_zone_device_register(buffer, + phy_dev_entry->tzone = thermal_zone_device_register("x86_pkg_temp", thres_count, (thres_count == MAX_NUMBER_OF_TRIPS) ? 0x03 : 0x01, - phy_dev_entry, &tzone_ops, NULL, 0, 0); + phy_dev_entry, &tzone_ops, &pkg_temp_tz_params, 0, 0); if (IS_ERR(phy_dev_entry->tzone)) { err = PTR_ERR(phy_dev_entry->tzone); goto err_ret_free; diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index be33d2b0613b..7e0b62602632 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1041,8 +1041,7 @@ static int sci_notifier(struct notifier_block *self, sci_port = container_of(self, struct sci_port, freq_transition); - if ((phase == CPUFREQ_POSTCHANGE) || - (phase == CPUFREQ_RESUMECHANGE)) { + if (phase == CPUFREQ_POSTCHANGE) { struct uart_port *port = &sci_port->port; spin_lock_irqsave(&port->lock, flags); diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index cf86e729532b..dc697cee248a 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -433,13 +433,10 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign unsigned long flags; int locked = 1; - local_irq_save(flags); - if (port->sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&port->lock); - } else - spin_lock(&port->lock); + if (port->sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); while (n > 0) { unsigned long ra = __pa(con_write_page); @@ -470,8 +467,7 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign } if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&port->lock, flags); } static inline void sunhv_console_putchar(struct uart_port *port, char c) @@ -492,7 +488,10 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig unsigned long flags; int i, locked = 1; - local_irq_save(flags); + if (port->sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); if (port->sysrq) { locked = 0; } else if (oops_in_progress) { @@ -507,8 +506,7 @@ static void sunhv_console_write_bychar(struct console *con, const char *s, unsig } if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&port->lock, flags); } static struct console sunhv_console = { diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c index 380fb5355cb2..5faa8e905e98 100644 --- a/drivers/tty/serial/sunsab.c +++ b/drivers/tty/serial/sunsab.c @@ -844,20 +844,16 @@ static void sunsab_console_write(struct console *con, const char *s, unsigned n) unsigned long flags; int locked = 1; - local_irq_save(flags); - if (up->port.sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); + if (up->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&up->port.lock, flags); + else + spin_lock_irqsave(&up->port.lock, flags); uart_console_write(&up->port, s, n, sunsab_console_putchar); sunsab_tec_wait(up); if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&up->port.lock, flags); } static int sunsab_console_setup(struct console *con, char *options) diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index db79b76f5c8e..9a0f24f83720 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1295,13 +1295,10 @@ static void sunsu_console_write(struct console *co, const char *s, unsigned int ier; int locked = 1; - local_irq_save(flags); - if (up->port.sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); + if (up->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&up->port.lock, flags); + else + spin_lock_irqsave(&up->port.lock, flags); /* * First save the UER then disable the interrupts @@ -1319,8 +1316,7 @@ static void sunsu_console_write(struct console *co, const char *s, serial_out(up, UART_IER, ier); if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&up->port.lock, flags); } /* diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c index 45a8c6aa5837..a2c40ed287d2 100644 --- a/drivers/tty/serial/sunzilog.c +++ b/drivers/tty/serial/sunzilog.c @@ -1195,20 +1195,16 @@ sunzilog_console_write(struct console *con, const char *s, unsigned int count) unsigned long flags; int locked = 1; - local_irq_save(flags); - if (up->port.sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); + if (up->port.sysrq || oops_in_progress) + locked = spin_trylock_irqsave(&up->port.lock, flags); + else + spin_lock_irqsave(&up->port.lock, flags); uart_console_write(&up->port, s, count, sunzilog_putchar); udelay(2); if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); + spin_unlock_irqrestore(&up->port.lock, flags); } static int __init sunzilog_console_setup(struct console *con, char *options) diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c index d8a55e87877f..0ffb0cbe2823 100644 --- a/drivers/tty/tty_ldsem.c +++ b/drivers/tty/tty_ldsem.c @@ -39,17 +39,10 @@ lock_acquire(&(l)->dep_map, s, t, r, c, n, i) # define __rel(l, n, i) \ lock_release(&(l)->dep_map, n, i) -# ifdef CONFIG_PROVE_LOCKING -# define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 2, NULL, i) -# define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 2, n, i) -# define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 2, NULL, i) -# define lockdep_release(l, n, i) __rel(l, n, i) -# else -# define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 1, NULL, i) -# define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 1, n, i) -# define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 1, NULL, i) -# define lockdep_release(l, n, i) __rel(l, n, i) -# endif +#define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 1, NULL, i) +#define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 1, n, i) +#define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 1, NULL, i) +#define lockdep_release(l, n, i) __rel(l, n, i) #else # define lockdep_acquire(l, s, t, i) do { } while (0) # define lockdep_acquire_nest(l, s, t, n, i) do { } while (0) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 8d72f0c65937..062967c90b2a 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -717,6 +717,10 @@ int usb_get_configuration(struct usb_device *dev) result = -ENOMEM; goto err; } + + if (dev->quirks & USB_QUIRK_DELAY_INIT) + msleep(100); + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length); if (result < 0) { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 64ea21971be2..5cbf78d0be25 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1040,7 +1040,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) */ if (type == HUB_INIT) { delay = hub_power_on(hub, false); - PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2); + INIT_DELAYED_WORK(&hub->init_work, hub_init_func2); schedule_delayed_work(&hub->init_work, msecs_to_jiffies(delay)); @@ -1194,7 +1194,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) /* Don't do a long sleep inside a workqueue routine */ if (type == HUB_INIT2) { - PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3); + INIT_DELAYED_WORK(&hub->init_work, hub_init_func3); schedule_delayed_work(&hub->init_work, msecs_to_jiffies(delay)); return; /* Continues at init3: below */ diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 8f37063c0a49..739ee8e8bdfd 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -47,6 +47,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* Microsoft LifeCam-VX700 v2.0 */ { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech HD Pro Webcams C920 and C930e */ + { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT }, + { USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT }, + /* Logitech Quickcam Fusion */ { USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index b269dbd47fc4..b1d7ee6e40b7 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -29,7 +29,6 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/extcon.h> -#include <linux/extcon/of_extcon.h> #include <linux/regulator/consumer.h> #include <linux/usb/otg.h> @@ -522,7 +521,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) dwc3_omap_enable_irqs(omap); if (of_property_read_bool(node, "extcon")) { - edev = of_extcon_get_extcon_dev(dev, 0); + edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(edev)) { dev_vdbg(dev, "couldn't get extcon device\n"); ret = -EPROBE_DEFER; diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c index 36d4bb23087f..807b31c0edc3 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -664,9 +664,10 @@ static int f_midi_register_card(struct f_midi *midi) .dev_free = f_midi_snd_free, }; - err = snd_card_create(midi->index, midi->id, THIS_MODULE, 0, &card); + err = snd_card_new(&midi->gadget->dev, midi->index, midi->id, + THIS_MODULE, 0, &card); if (err < 0) { - ERROR(midi, "snd_card_create() failed\n"); + ERROR(midi, "snd_card_new() failed\n"); goto fail; } midi->card = card; @@ -703,8 +704,6 @@ static int f_midi_register_card(struct f_midi *midi) snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops); - snd_card_set_dev(card, &midi->gadget->dev); - /* register it - we're ready to go */ err = snd_card_register(card); if (err < 0) { diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c index 2f23566e53d8..bc23040c7790 100644 --- a/drivers/usb/gadget/f_uac2.c +++ b/drivers/usb/gadget/f_uac2.c @@ -394,7 +394,7 @@ static int snd_uac2_probe(struct platform_device *pdev) int err; /* Choose any slot, with no id */ - err = snd_card_create(-1, NULL, THIS_MODULE, 0, &card); + err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card); if (err < 0) return err; @@ -421,8 +421,6 @@ static int snd_uac2_probe(struct platform_device *pdev) strcpy(card->shortname, "UAC2_Gadget"); sprintf(card->longname, "UAC2_Gadget %i", pdev->id); - snd_card_set_dev(card, &pdev->dev); - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6fe577d46fa2..924a6ccdb622 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4733,6 +4733,9 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; + /* support to build packet from discontinuous buffers */ + hcd->self.no_sg_constraint = 1; + /* XHCI controllers don't stop the ep queue on short packets :| */ hcd->self.no_stop_on_short = 1; @@ -4757,14 +4760,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* xHCI private pointer was set in xhci_pci_probe for the second * registered roothub. */ - xhci = hcd_to_xhci(hcd); - /* - * Support arbitrarily aligned sg-list entries on hosts without - * TD fragment rules (which are currently unsupported). - */ - if (xhci->hci_version < 0x100) - hcd->self.no_sg_constraint = 1; - return 0; } @@ -4793,9 +4788,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x96) xhci->quirks |= XHCI_SPURIOUS_SUCCESS; - if (xhci->hci_version < 0x100) - hcd->self.no_sg_constraint = 1; - /* Make sure the HC is halted. */ retval = xhci_halt(xhci); if (retval) diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index 210357691dc0..9dd49c9839ac 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -482,15 +482,19 @@ static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix) for (i = 0; i < nvec; i++) vdev->msix[i].entry = i; - ret = pci_enable_msix(pdev, vdev->msix, nvec); - if (ret) { + ret = pci_enable_msix_range(pdev, vdev->msix, 1, nvec); + if (ret < nvec) { + if (ret > 0) + pci_disable_msix(pdev); kfree(vdev->msix); kfree(vdev->ctx); return ret; } } else { - ret = pci_enable_msi_block(pdev, nvec); - if (ret) { + ret = pci_enable_msi_range(pdev, 1, nvec); + if (ret < nvec) { + if (ret > 0) + pci_disable_msi(pdev); kfree(vdev->ctx); return ret; } diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 4fb7a8f83c8a..54af4e933695 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -186,12 +186,12 @@ static bool is_invalid_reserved_pfn(unsigned long pfn) if (pfn_valid(pfn)) { bool reserved; struct page *tail = pfn_to_page(pfn); - struct page *head = compound_trans_head(tail); + struct page *head = compound_head(tail); reserved = !!(PageReserved(head)); if (head != tail) { /* * "head" is not a dangling pointer - * (compound_trans_head takes care of that) + * (compound_head takes care of that) * but the hugepage may have been split * from under us (and we may not hold a * reference count on the head page so it can diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index a0fa5de210cf..e1e22e0f01e8 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -505,9 +505,13 @@ static int get_rx_bufs(struct vhost_virtqueue *vq, r = -ENOBUFS; goto err; } - d = vhost_get_vq_desc(vq->dev, vq, vq->iov + seg, + r = vhost_get_vq_desc(vq->dev, vq, vq->iov + seg, ARRAY_SIZE(vq->iov) - seg, &out, &in, log, log_num); + if (unlikely(r < 0)) + goto err; + + d = r; if (d == vq->num) { r = 0; goto err; @@ -532,6 +536,12 @@ static int get_rx_bufs(struct vhost_virtqueue *vq, *iovcount = seg; if (unlikely(log)) *log_num = nlogs; + + /* Detect overrun */ + if (unlikely(datalen > 0)) { + r = UIO_MAXIOV + 1; + goto err; + } return headcount; err: vhost_discard_vq_desc(vq, headcount); @@ -587,6 +597,14 @@ static void handle_rx(struct vhost_net *net) /* On error, stop handling until the next kick. */ if (unlikely(headcount < 0)) break; + /* On overrun, truncate and discard */ + if (unlikely(headcount > UIO_MAXIOV)) { + msg.msg_iovlen = 1; + err = sock->ops->recvmsg(NULL, sock, &msg, + 1, MSG_DONTWAIT | MSG_TRUNC); + pr_debug("Discarded rx packet: len %zd\n", sock_len); + continue; + } /* OK, now we need to know about added descriptors. */ if (!headcount) { if (unlikely(vhost_enable_notify(&net->dev, vq))) { diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index dade5b7699bc..97a8f3a12a7b 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -27,12 +27,6 @@ config VGASTATE tristate default n -config VIDEO_OUTPUT_CONTROL - tristate "Lowlevel video output switch controls" - help - This framework adds support for low-level control of the video - output switch. - config VIDEOMODE_HELPERS bool diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ae17ddf49a00..08d6a4ab3ace 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -172,8 +172,6 @@ obj-$(CONFIG_FB_SIMPLE) += simplefb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o -#video output switch sysfs driver -obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o obj-$(CONFIG_VIDEOMODE_HELPERS) += display_timing.o videomode.o ifeq ($(CONFIG_OF),y) obj-$(CONFIG_VIDEOMODE_HELPERS) += of_display_timing.o of_videomode.o diff --git a/drivers/video/hyperv_fb.c b/drivers/video/hyperv_fb.c index 130708f96430..e23392ec5af3 100644 --- a/drivers/video/hyperv_fb.c +++ b/drivers/video/hyperv_fb.c @@ -42,6 +42,7 @@ #include <linux/completion.h> #include <linux/fb.h> #include <linux/pci.h> +#include <linux/efi.h> #include <linux/hyperv.h> @@ -212,6 +213,7 @@ struct synthvid_msg { struct hvfb_par { struct fb_info *info; + struct resource mem; bool fb_ready; /* fb device is ready */ struct completion wait; u32 synthvid_version; @@ -460,13 +462,13 @@ static int synthvid_connect_vsp(struct hv_device *hdev) goto error; } - if (par->synthvid_version == SYNTHVID_VERSION_WIN7) { + if (par->synthvid_version == SYNTHVID_VERSION_WIN7) screen_depth = SYNTHVID_DEPTH_WIN7; - screen_fb_size = SYNTHVID_FB_SIZE_WIN7; - } else { + else screen_depth = SYNTHVID_DEPTH_WIN8; - screen_fb_size = SYNTHVID_FB_SIZE_WIN8; - } + + screen_fb_size = hdev->channel->offermsg.offer. + mmio_megabytes * 1024 * 1024; return 0; @@ -627,26 +629,46 @@ static void hvfb_get_option(struct fb_info *info) /* Get framebuffer memory from Hyper-V video pci space */ static int hvfb_getmem(struct fb_info *info) { - struct pci_dev *pdev; - ulong fb_phys; + struct hvfb_par *par = info->par; + struct pci_dev *pdev = NULL; void __iomem *fb_virt; + int gen2vm = efi_enabled(EFI_BOOT); + int ret; - pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, + par->mem.name = KBUILD_MODNAME; + par->mem.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (gen2vm) { + ret = allocate_resource(&hyperv_mmio, &par->mem, + screen_fb_size, + 0, -1, + screen_fb_size, + NULL, NULL); + if (ret != 0) { + pr_err("Unable to allocate framebuffer memory\n"); + return -ENODEV; + } + } else { + pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, PCI_DEVICE_ID_HYPERV_VIDEO, NULL); - if (!pdev) { - pr_err("Unable to find PCI Hyper-V video\n"); - return -ENODEV; - } + if (!pdev) { + pr_err("Unable to find PCI Hyper-V video\n"); + return -ENODEV; + } - if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) || - pci_resource_len(pdev, 0) < screen_fb_size) - goto err1; + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) || + pci_resource_len(pdev, 0) < screen_fb_size) + goto err1; - fb_phys = pci_resource_end(pdev, 0) - screen_fb_size + 1; - if (!request_mem_region(fb_phys, screen_fb_size, KBUILD_MODNAME)) - goto err1; + par->mem.end = pci_resource_end(pdev, 0); + par->mem.start = par->mem.end - screen_fb_size + 1; + ret = request_resource(&pdev->resource[0], &par->mem); + if (ret != 0) { + pr_err("Unable to request framebuffer memory\n"); + goto err1; + } + } - fb_virt = ioremap(fb_phys, screen_fb_size); + fb_virt = ioremap(par->mem.start, screen_fb_size); if (!fb_virt) goto err2; @@ -654,30 +676,44 @@ static int hvfb_getmem(struct fb_info *info) if (!info->apertures) goto err3; - info->apertures->ranges[0].base = pci_resource_start(pdev, 0); - info->apertures->ranges[0].size = pci_resource_len(pdev, 0); - info->fix.smem_start = fb_phys; + if (gen2vm) { + info->apertures->ranges[0].base = screen_info.lfb_base; + info->apertures->ranges[0].size = screen_info.lfb_size; + remove_conflicting_framebuffers(info->apertures, + KBUILD_MODNAME, false); + } else { + info->apertures->ranges[0].base = pci_resource_start(pdev, 0); + info->apertures->ranges[0].size = pci_resource_len(pdev, 0); + } + + info->fix.smem_start = par->mem.start; info->fix.smem_len = screen_fb_size; info->screen_base = fb_virt; info->screen_size = screen_fb_size; - pci_dev_put(pdev); + if (!gen2vm) + pci_dev_put(pdev); + return 0; err3: iounmap(fb_virt); err2: - release_mem_region(fb_phys, screen_fb_size); + release_resource(&par->mem); err1: - pci_dev_put(pdev); + if (!gen2vm) + pci_dev_put(pdev); + return -ENOMEM; } /* Release the framebuffer */ static void hvfb_putmem(struct fb_info *info) { + struct hvfb_par *par = info->par; + iounmap(info->screen_base); - release_mem_region(info->fix.smem_start, screen_fb_size); + release_resource(&par->mem); } diff --git a/drivers/video/output.c b/drivers/video/output.c deleted file mode 100644 index 1446c49fe6af..000000000000 --- a/drivers/video/output.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * output.c - Display Output Switch driver - * - * Copyright (C) 2006 Luming Yu <luming.yu@intel.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. - * - * 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/module.h> -#include <linux/video_output.h> -#include <linux/slab.h> -#include <linux/err.h> -#include <linux/ctype.h> - - -MODULE_DESCRIPTION("Display Output Switcher Lowlevel Control Abstraction"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Luming Yu <luming.yu@intel.com>"); - -static ssize_t state_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - ssize_t ret_size = 0; - struct output_device *od = to_output_device(dev); - if (od->props) - ret_size = sprintf(buf,"%.8x\n",od->props->get_status(od)); - return ret_size; -} - -static ssize_t state_store(struct device *dev, struct device_attribute *attr, - const char *buf,size_t count) -{ - char *endp; - struct output_device *od = to_output_device(dev); - int request_state = simple_strtoul(buf,&endp,0); - size_t size = endp - buf; - - if (isspace(*endp)) - size++; - if (size != count) - return -EINVAL; - - if (od->props) { - od->request_state = request_state; - od->props->set_state(od); - } - return count; -} -static DEVICE_ATTR_RW(state); - -static void video_output_release(struct device *dev) -{ - struct output_device *od = to_output_device(dev); - kfree(od); -} - -static struct attribute *video_output_attrs[] = { - &dev_attr_state.attr, - NULL, -}; -ATTRIBUTE_GROUPS(video_output); - -static struct class video_output_class = { - .name = "video_output", - .dev_release = video_output_release, - .dev_groups = video_output_groups, -}; - -struct output_device *video_output_register(const char *name, - struct device *dev, - void *devdata, - struct output_properties *op) -{ - struct output_device *new_dev; - int ret_code = 0; - - new_dev = kzalloc(sizeof(struct output_device),GFP_KERNEL); - if (!new_dev) { - ret_code = -ENOMEM; - goto error_return; - } - new_dev->props = op; - new_dev->dev.class = &video_output_class; - new_dev->dev.parent = dev; - dev_set_name(&new_dev->dev, "%s", name); - dev_set_drvdata(&new_dev->dev, devdata); - ret_code = device_register(&new_dev->dev); - if (ret_code) { - kfree(new_dev); - goto error_return; - } - return new_dev; - -error_return: - return ERR_PTR(ret_code); -} -EXPORT_SYMBOL(video_output_register); - -void video_output_unregister(struct output_device *dev) -{ - if (!dev) - return; - device_unregister(&dev->dev); -} -EXPORT_SYMBOL(video_output_unregister); - -static void __exit video_output_class_exit(void) -{ - class_unregister(&video_output_class); -} - -static int __init video_output_class_init(void) -{ - return class_register(&video_output_class); -} - -postcore_initcall(video_output_class_init); -module_exit(video_output_class_exit); diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 256fba7f4641..1f38445014c1 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -190,7 +190,7 @@ static int uvesafb_exec(struct uvesafb_ktask *task) uvfb_tasks[seq] = task; mutex_unlock(&uvfb_lock); - err = cn_netlink_send(m, 0, GFP_KERNEL); + err = cn_netlink_send(m, 0, 0, GFP_KERNEL); if (err == -ESRCH) { /* * Try to start the userspace helper if sending @@ -204,7 +204,7 @@ static int uvesafb_exec(struct uvesafb_ktask *task) "helper is installed and executable\n"); } else { v86d_started = 1; - err = cn_netlink_send(m, 0, gfp_any()); + err = cn_netlink_send(m, 0, 0, gfp_any()); if (err == -ENOBUFS) err = 0; } diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c index 1b5d48c578e1..bfb2d3f06738 100644 --- a/drivers/vme/bridges/vme_ca91cx42.c +++ b/drivers/vme/bridges/vme_ca91cx42.c @@ -869,14 +869,13 @@ static ssize_t ca91cx42_master_read(struct vme_master_resource *image, spin_lock(&image->lock); - /* The following code handles VME address alignment problem - * in order to assure the maximal data width cycle. - * We cannot use memcpy_xxx directly here because it - * may cut data transfer in 8-bits cycles, thus making - * D16 cycle impossible. - * From the other hand, the bridge itself assures that - * maximal configured data cycle is used and splits it - * automatically for non-aligned addresses. + /* The following code handles VME address alignment. We cannot use + * memcpy_xxx here because it may cut data transfers in to 8-bit + * cycles when D16 or D32 cycles are required on the VME bus. + * On the other hand, the bridge itself assures that the maximum data + * cycle configured for the transfer is used and splits it + * automatically for non-aligned addresses, so we don't want the + * overhead of needlessly forcing small transfers for the entire cycle. */ if ((uintptr_t)addr & 0x1) { *(u8 *)buf = ioread8(addr); @@ -896,9 +895,9 @@ static ssize_t ca91cx42_master_read(struct vme_master_resource *image, } count32 = (count - done) & ~0x3; - if (count32 > 0) { - memcpy_fromio(buf + done, addr + done, (unsigned int)count); - done += count32; + while (done < count32) { + *(u32 *)(buf + done) = ioread32(addr + done); + done += 4; } if ((count - done) & 0x2) { @@ -930,7 +929,7 @@ static ssize_t ca91cx42_master_write(struct vme_master_resource *image, spin_lock(&image->lock); /* Here we apply for the same strategy we do in master_read - * function in order to assure D16 cycle when required. + * function in order to assure the correct cycles. */ if ((uintptr_t)addr & 0x1) { iowrite8(*(u8 *)buf, addr); @@ -950,9 +949,9 @@ static ssize_t ca91cx42_master_write(struct vme_master_resource *image, } count32 = (count - done) & ~0x3; - if (count32 > 0) { - memcpy_toio(addr + done, buf + done, count32); - done += count32; + while (done < count32) { + iowrite32(*(u32 *)(buf + done), addr + done); + done += 4; } if ((count - done) & 0x2) { diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c index 9911cd5fddb5..06990c6a1a69 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/vme/bridges/vme_tsi148.c @@ -1276,8 +1276,8 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf, spin_lock(&image->lock); /* The following code handles VME address alignment. We cannot use - * memcpy_xxx directly here because it may cut small data transfers in - * to 8-bit cycles, thus making D16 cycle impossible. + * memcpy_xxx here because it may cut data transfers in to 8-bit + * cycles when D16 or D32 cycles are required on the VME bus. * On the other hand, the bridge itself assures that the maximum data * cycle configured for the transfer is used and splits it * automatically for non-aligned addresses, so we don't want the @@ -1301,9 +1301,9 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf, } count32 = (count - done) & ~0x3; - if (count32 > 0) { - memcpy_fromio(buf + done, addr + done, count32); - done += count32; + while (done < count32) { + *(u32 *)(buf + done) = ioread32(addr + done); + done += 4; } if ((count - done) & 0x2) { @@ -1363,7 +1363,7 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf, spin_lock(&image->lock); /* Here we apply for the same strategy we do in master_read - * function in order to assure D16 cycle when required. + * function in order to assure the correct cycles. */ if ((uintptr_t)addr & 0x1) { iowrite8(*(u8 *)buf, addr); @@ -1383,9 +1383,9 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf, } count32 = (count - done) & ~0x3; - if (count32 > 0) { - memcpy_toio(addr + done, buf + done, count32); - done += count32; + while (done < count32) { + iowrite32(*(u32 *)(buf + done), addr + done); + done += 4; } if ((count - done) & 0x2) { diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index efc7f075fcbe..1708b2300c7a 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -36,13 +36,12 @@ config W1_MASTER_DS2482 config W1_MASTER_MXC tristate "Freescale MXC 1-wire busmaster" - depends on W1 && ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST help Say Y here to enable MXC 1-wire host config W1_MASTER_DS1WM tristate "Maxim DS1WM 1-wire busmaster" - depends on W1 help Say Y here to enable the DS1WM 1-wire driver, such as that in HP iPAQ devices like h5xxx, h2200, and ASIC3-based like diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 4f7e1d770f81..7404ad3062b7 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c @@ -1,5 +1,5 @@ /* - * dscore.c + * ds2490.c USB to one wire bridge * * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net> * @@ -28,6 +28,10 @@ #include "../w1_int.h" #include "../w1.h" +/* USB Standard */ +/* USB Control request vendor type */ +#define VENDOR 0x40 + /* COMMAND TYPE CODES */ #define CONTROL_CMD 0x00 #define COMM_CMD 0x01 @@ -107,6 +111,8 @@ #define ST_HALT 0x10 /* DS2490 is currently halted */ #define ST_IDLE 0x20 /* DS2490 is currently idle */ #define ST_EPOF 0x80 +/* Status transfer size, 16 bytes status, 16 byte result flags */ +#define ST_SIZE 0x20 /* Result Register flags */ #define RR_DETECT 0xA5 /* New device detected */ @@ -198,7 +204,7 @@ static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index) int err; err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), - CONTROL_CMD, 0x40, value, index, NULL, 0, 1000); + CONTROL_CMD, VENDOR, value, index, NULL, 0, 1000); if (err < 0) { printk(KERN_ERR "Failed to send command control message %x.%x: err=%d.\n", value, index, err); @@ -213,7 +219,7 @@ static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index) int err; err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), - MODE_CMD, 0x40, value, index, NULL, 0, 1000); + MODE_CMD, VENDOR, value, index, NULL, 0, 1000); if (err < 0) { printk(KERN_ERR "Failed to send mode control message %x.%x: err=%d.\n", value, index, err); @@ -228,7 +234,7 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index) int err; err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), - COMM_CMD, 0x40, value, index, NULL, 0, 1000); + COMM_CMD, VENDOR, value, index, NULL, 0, 1000); if (err < 0) { printk(KERN_ERR "Failed to send control message %x.%x: err=%d.\n", value, index, err); @@ -246,7 +252,8 @@ static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, memset(st, 0, sizeof(*st)); count = 0; - err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_STATUS]), buf, size, &count, 100); + err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev, + dev->ep[EP_STATUS]), buf, size, &count, 100); if (err < 0) { printk(KERN_ERR "Failed to read 1-wire data from 0x%x: err=%d.\n", dev->ep[EP_STATUS], err); return err; @@ -353,7 +360,7 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]), buf, size, &count, 1000); if (err < 0) { - u8 buf[0x20]; + u8 buf[ST_SIZE]; int count; printk(KERN_INFO "Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); @@ -398,7 +405,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit) { struct ds_status st; int count = 0, err = 0; - u8 buf[0x20]; + u8 buf[ST_SIZE]; do { err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0); @@ -450,10 +457,11 @@ int ds_detect(struct ds_device *dev, struct ds_status *st) static int ds_wait_status(struct ds_device *dev, struct ds_status *st) { - u8 buf[0x20]; + u8 buf[ST_SIZE]; int err, count = 0; do { + st->status = 0; err = ds_recv_status_nodump(dev, st, buf, sizeof(buf)); #if 0 if (err >= 0) { @@ -464,7 +472,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st) printk("\n"); } #endif - } while (!(buf[0x08] & ST_IDLE) && !(err < 0) && ++count < 100); + } while (!(st->status & ST_IDLE) && !(err < 0) && ++count < 100); if (err >= 16 && st->status & ST_EPOF) { printk(KERN_INFO "Resetting device after ST_EPOF.\n"); @@ -690,37 +698,106 @@ static int ds_write_block(struct ds_device *dev, u8 *buf, int len) return !(err == len); } -#if 0 - -static int ds_search(struct ds_device *dev, u64 init, u64 *buf, u8 id_number, int conditional_search) +static void ds9490r_search(void *data, struct w1_master *master, + u8 search_type, w1_slave_found_callback callback) { + /* When starting with an existing id, the first id returned will + * be that device (if it is still on the bus most likely). + * + * If the number of devices found is less than or equal to the + * search_limit, that number of IDs will be returned. If there are + * more, search_limit IDs will be returned followed by a non-zero + * discrepency value. + */ + struct ds_device *dev = data; int err; u16 value, index; struct ds_status st; + u8 st_buf[ST_SIZE]; + int search_limit; + int found = 0; + int i; - memset(buf, 0, sizeof(buf)); + /* DS18b20 spec, 13.16 ms per device, 75 per second, sleep for + * discovering 8 devices (1 bulk transfer and 1/2 FIFO size) at a time. + */ + const unsigned long jtime = msecs_to_jiffies(1000*8/75); + /* FIFO 128 bytes, bulk packet size 64, read a multiple of the + * packet size. + */ + u64 buf[2*64/8]; - err = ds_send_data(ds_dev, (unsigned char *)&init, 8); - if (err) - return err; + mutex_lock(&master->bus_mutex); - ds_wait_status(ds_dev, &st); + /* address to start searching at */ + if (ds_send_data(dev, (u8 *)&master->search_id, 8) < 0) + goto search_out; + master->search_id = 0; - value = COMM_SEARCH_ACCESS | COMM_IM | COMM_SM | COMM_F | COMM_RTS; - index = (conditional_search ? 0xEC : 0xF0) | (id_number << 8); - err = ds_send_control(ds_dev, value, index); - if (err) - return err; + value = COMM_SEARCH_ACCESS | COMM_IM | COMM_RST | COMM_SM | COMM_F | + COMM_RTS; + search_limit = master->max_slave_count; + if (search_limit > 255) + search_limit = 0; + index = search_type | (search_limit << 8); + if (ds_send_control(dev, value, index) < 0) + goto search_out; - ds_wait_status(ds_dev, &st); + do { + schedule_timeout(jtime); - err = ds_recv_data(ds_dev, (unsigned char *)buf, 8*id_number); - if (err < 0) - return err; + if (ds_recv_status_nodump(dev, &st, st_buf, sizeof(st_buf)) < + sizeof(st)) { + break; + } - return err/8; + if (st.data_in_buffer_status) { + /* Bulk in can receive partial ids, but when it does + * they fail crc and will be discarded anyway. + * That has only been seen when status in buffer + * is 0 and bulk is read anyway, so don't read + * bulk without first checking if status says there + * is data to read. + */ + err = ds_recv_data(dev, (u8 *)buf, sizeof(buf)); + if (err < 0) + break; + for (i = 0; i < err/8; ++i) { + ++found; + if (found <= search_limit) + callback(master, buf[i]); + /* can't know if there will be a discrepancy + * value after until the next id */ + if (found == search_limit) + master->search_id = buf[i]; + } + } + + if (test_bit(W1_ABORT_SEARCH, &master->flags)) + break; + } while (!(st.status & (ST_IDLE | ST_HALT))); + + /* only continue the search if some weren't found */ + if (found <= search_limit) { + master->search_id = 0; + } else if (!test_bit(W1_WARN_MAX_COUNT, &master->flags)) { + /* Only max_slave_count will be scanned in a search, + * but it will start where it left off next search + * until all ids are identified and then it will start + * over. A continued search will report the previous + * last id as the first id (provided it is still on the + * bus). + */ + dev_info(&dev->udev->dev, "%s: max_slave_count %d reached, " + "will continue next search.\n", __func__, + master->max_slave_count); + set_bit(W1_WARN_MAX_COUNT, &master->flags); + } +search_out: + mutex_unlock(&master->bus_mutex); } +#if 0 static int ds_match_access(struct ds_device *dev, u64 init) { int err; @@ -894,6 +971,7 @@ static int ds_w1_init(struct ds_device *dev) dev->master.write_block = &ds9490r_write_block; dev->master.reset_bus = &ds9490r_reset; dev->master.set_pullup = &ds9490r_set_pullup; + dev->master.search = &ds9490r_search; return w1_add_master_device(&dev->master); } @@ -910,15 +988,13 @@ static int ds_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *endpoint; struct usb_host_interface *iface_desc; struct ds_device *dev; - int i, err; + int i, err, alt; - dev = kmalloc(sizeof(struct ds_device), GFP_KERNEL); + dev = kzalloc(sizeof(struct ds_device), GFP_KERNEL); if (!dev) { printk(KERN_INFO "Failed to allocate new DS9490R structure.\n"); return -ENOMEM; } - dev->spu_sleep = 0; - dev->spu_bit = 0; dev->udev = usb_get_dev(udev); if (!dev->udev) { err = -ENOMEM; @@ -928,20 +1004,25 @@ static int ds_probe(struct usb_interface *intf, usb_set_intfdata(intf, dev); - err = usb_set_interface(dev->udev, intf->altsetting[0].desc.bInterfaceNumber, 3); + err = usb_reset_configuration(dev->udev); if (err) { - printk(KERN_ERR "Failed to set alternative setting 3 for %d interface: err=%d.\n", - intf->altsetting[0].desc.bInterfaceNumber, err); + dev_err(&dev->udev->dev, + "Failed to reset configuration: err=%d.\n", err); goto err_out_clear; } - err = usb_reset_configuration(dev->udev); + /* alternative 3, 1ms interrupt (greatly speeds search), 64 byte bulk */ + alt = 3; + err = usb_set_interface(dev->udev, + intf->altsetting[alt].desc.bInterfaceNumber, alt); if (err) { - printk(KERN_ERR "Failed to reset configuration: err=%d.\n", err); + dev_err(&dev->udev->dev, "Failed to set alternative setting %d " + "for %d interface: err=%d.\n", alt, + intf->altsetting[alt].desc.bInterfaceNumber, err); goto err_out_clear; } - iface_desc = &intf->altsetting[0]; + iface_desc = &intf->altsetting[alt]; if (iface_desc->desc.bNumEndpoints != NUM_EP-1) { printk(KERN_INFO "Num endpoints=%d. It is not DS9490R.\n", iface_desc->desc.bNumEndpoints); err = -EINVAL; diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index 1e5d94c5afc9..67b067a3e2ab 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -10,24 +10,16 @@ * 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-1301, USA. - * */ -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> #include <linux/clk.h> -#include <linux/slab.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include "../w1.h" #include "../w1_int.h" -#include "../w1_log.h" /* According to the mx27 Datasheet the reset procedure should take up to about * 1350us. We set the timeout to 500*100us = 50ms for sure */ @@ -36,13 +28,13 @@ /* * MXC W1 Register offsets */ -#define MXC_W1_CONTROL 0x00 -#define MXC_W1_TIME_DIVIDER 0x02 -#define MXC_W1_RESET 0x04 -#define MXC_W1_COMMAND 0x06 -#define MXC_W1_TXRX 0x08 -#define MXC_W1_INTERRUPT 0x0A -#define MXC_W1_INTERRUPT_EN 0x0C +#define MXC_W1_CONTROL 0x00 +# define MXC_W1_CONTROL_RDST BIT(3) +# define MXC_W1_CONTROL_WR(x) BIT(5 - (x)) +# define MXC_W1_CONTROL_PST BIT(6) +# define MXC_W1_CONTROL_RPP BIT(7) +#define MXC_W1_TIME_DIVIDER 0x02 +#define MXC_W1_RESET 0x04 struct mxc_w1_device { void __iomem *regs; @@ -61,12 +53,12 @@ static u8 mxc_w1_ds2_reset_bus(void *data) unsigned int timeout_cnt = 0; struct mxc_w1_device *dev = data; - __raw_writeb(0x80, (dev->regs + MXC_W1_CONTROL)); + writeb(MXC_W1_CONTROL_RPP, (dev->regs + MXC_W1_CONTROL)); while (1) { - reg_val = __raw_readb(dev->regs + MXC_W1_CONTROL); + reg_val = readb(dev->regs + MXC_W1_CONTROL); - if (((reg_val >> 7) & 0x1) == 0 || + if (!(reg_val & MXC_W1_CONTROL_RPP) || timeout_cnt > MXC_W1_RESET_TIMEOUT) break; else @@ -74,7 +66,7 @@ static u8 mxc_w1_ds2_reset_bus(void *data) udelay(100); } - return (reg_val >> 7) & 0x1; + return !!(reg_val & MXC_W1_CONTROL_PST); } /* @@ -90,16 +82,16 @@ static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) * datasheet. */ - __raw_writeb((1 << (5 - bit)), ctrl_addr); + writeb(MXC_W1_CONTROL_WR(bit), ctrl_addr); while (timeout_cnt--) { - if (!((__raw_readb(ctrl_addr) >> (5 - bit)) & 0x1)) + if (!(readb(ctrl_addr) & MXC_W1_CONTROL_WR(bit))) break; udelay(1); } - return ((__raw_readb(ctrl_addr)) >> 3) & 0x1; + return !!(readb(ctrl_addr) & MXC_W1_CONTROL_RDST); } static int mxc_w1_probe(struct platform_device *pdev) @@ -139,7 +131,7 @@ static int mxc_w1_probe(struct platform_device *pdev) if (err) return err; - __raw_writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER); + writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER); mdev->bus_master.data = mdev; mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus; @@ -177,6 +169,7 @@ MODULE_DEVICE_TABLE(of, mxc_w1_dt_ids); static struct platform_driver mxc_w1_driver = { .driver = { .name = "mxc_w1", + .owner = THIS_MODULE, .of_match_table = mxc_w1_dt_ids, }, .probe = mxc_w1_probe, diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index 9709b8b484ba..1d111e56c8c8 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -89,11 +89,22 @@ static int w1_gpio_probe_dt(struct platform_device *pdev) pdata->is_open_drain = 1; gpio = of_get_gpio(np, 0); - if (gpio < 0) + if (gpio < 0) { + if (gpio != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Failed to parse gpio property for data pin (%d)\n", + gpio); + return gpio; + } pdata->pin = gpio; - pdata->ext_pullup_enable_pin = of_get_gpio(np, 1); + gpio = of_get_gpio(np, 1); + if (gpio == -EPROBE_DEFER) + return gpio; + /* ignore other errors as the pullup gpio is optional */ + pdata->ext_pullup_enable_pin = gpio; + pdev->dev.platform_data = pdata; return 0; @@ -107,10 +118,8 @@ static int w1_gpio_probe(struct platform_device *pdev) if (of_have_populated_dt()) { err = w1_gpio_probe_dt(pdev); - if (err < 0) { - dev_err(&pdev->dev, "Failed to parse DT\n"); + if (err < 0) return err; - } } pdata = dev_get_platdata(&pdev->dev); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 5e6a3c9e510b..1cdce80b6abf 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -72,7 +72,6 @@ config W1_SLAVE_DS2433_CRC config W1_SLAVE_DS2760 tristate "Dallas 2760 battery monitor chip (HP iPAQ & others)" - depends on W1 help If you enable this you will have the DS2760 battery monitor chip support. @@ -85,7 +84,6 @@ config W1_SLAVE_DS2760 config W1_SLAVE_DS2780 tristate "Dallas 2780 battery monitor chip" - depends on W1 help If you enable this you will have the DS2780 battery monitor chip support. @@ -98,7 +96,6 @@ config W1_SLAVE_DS2780 config W1_SLAVE_DS2781 tristate "Dallas 2781 battery monitor chip" - depends on W1 help If you enable this you will have the DS2781 battery monitor chip support. @@ -111,7 +108,6 @@ config W1_SLAVE_DS2781 config W1_SLAVE_DS28E04 tristate "4096-Bit Addressable 1-Wire EEPROM with PIO (DS28E04-100)" - depends on W1 select CRC16 help If you enable this you will have the DS28E04-100 @@ -124,7 +120,6 @@ config W1_SLAVE_DS28E04 config W1_SLAVE_BQ27000 tristate "BQ27000 slave support" - depends on W1 help Say Y here if you want to use a hdq bq27000 slave support. diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 8b5ff33f72cf..1f11a20a8ab9 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -27,6 +27,7 @@ #include <linux/sched.h> #include <linux/device.h> #include <linux/types.h> +#include <linux/slab.h> #include <linux/delay.h> #include "../w1.h" @@ -58,6 +59,19 @@ MODULE_ALIAS("w1-family-" __stringify(W1_THERM_DS28EA00)); static int w1_strong_pullup = 1; module_param_named(strong_pullup, w1_strong_pullup, int, 0); +static int w1_therm_add_slave(struct w1_slave *sl) +{ + sl->family_data = kzalloc(9, GFP_KERNEL); + if (!sl->family_data) + return -ENOMEM; + return 0; +} + +static void w1_therm_remove_slave(struct w1_slave *sl) +{ + kfree(sl->family_data); + sl->family_data = NULL; +} static ssize_t w1_slave_show(struct device *device, struct device_attribute *attr, char *buf); @@ -71,6 +85,8 @@ static struct attribute *w1_therm_attrs[] = { ATTRIBUTE_GROUPS(w1_therm); static struct w1_family_ops w1_therm_fops = { + .add_slave = w1_therm_add_slave, + .remove_slave = w1_therm_remove_slave, .groups = w1_therm_groups, }; @@ -253,12 +269,13 @@ static ssize_t w1_slave_show(struct device *device, c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", crc, (verdict) ? "YES" : "NO"); if (verdict) - memcpy(sl->rom, rom, sizeof(sl->rom)); + memcpy(sl->family_data, rom, sizeof(rom)); else dev_warn(device, "Read failed CRC check\n"); for (i = 0; i < 9; ++i) - c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", sl->rom[i]); + c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", + ((u8 *)sl->family_data)[i]); c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n", w1_convert_temp(rom, sl->family->fid)); diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 66efa96c4603..b96f61b15dc6 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -46,18 +46,29 @@ MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol."); static int w1_timeout = 10; -int w1_max_slave_count = 10; +int w1_max_slave_count = 64; int w1_max_slave_ttl = 10; module_param_named(timeout, w1_timeout, int, 0); +MODULE_PARM_DESC(timeout, "time in seconds between automatic slave searches"); +/* A search stops when w1_max_slave_count devices have been found in that + * search. The next search will start over and detect the same set of devices + * on a static 1-wire bus. Memory is not allocated based on this number, just + * on the number of devices known to the kernel. Having a high number does not + * consume additional resources. As a special case, if there is only one + * device on the network and w1_max_slave_count is set to 1, the device id can + * be read directly skipping the normal slower search process. + */ module_param_named(max_slave_count, w1_max_slave_count, int, 0); +MODULE_PARM_DESC(max_slave_count, + "maximum number of slaves detected in a search"); module_param_named(slave_ttl, w1_max_slave_ttl, int, 0); +MODULE_PARM_DESC(slave_ttl, + "Number of searches not seeing a slave before it will be removed"); DEFINE_MUTEX(w1_mlock); LIST_HEAD(w1_masters); -static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn); - static int w1_master_match(struct device *dev, struct device_driver *drv) { return 1; @@ -81,19 +92,10 @@ static void w1_slave_release(struct device *dev) { struct w1_slave *sl = dev_to_w1_slave(dev); - dev_dbg(dev, "%s: Releasing %s.\n", __func__, sl->name); - - while (atomic_read(&sl->refcnt)) { - dev_dbg(dev, "Waiting for %s to become free: refcnt=%d.\n", - sl->name, atomic_read(&sl->refcnt)); - if (msleep_interruptible(1000)) - flush_signals(current); - } + dev_dbg(dev, "%s: Releasing %s [%p]\n", __func__, sl->name, sl); w1_family_put(sl->family); sl->master->slave_count--; - - complete(&sl->released); } static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -243,7 +245,9 @@ static ssize_t w1_master_attribute_store_search(struct device * dev, mutex_lock(&md->mutex); md->search_count = tmp; mutex_unlock(&md->mutex); - wake_up_process(md->thread); + /* Only wake if it is going to be searching. */ + if (tmp) + wake_up_process(md->thread); return count; } @@ -277,7 +281,6 @@ static ssize_t w1_master_attribute_store_pullup(struct device *dev, mutex_lock(&md->mutex); md->enable_pullup = tmp; mutex_unlock(&md->mutex); - wake_up_process(md->thread); return count; } @@ -314,6 +317,24 @@ static ssize_t w1_master_attribute_show_timeout(struct device *dev, struct devic return count; } +static ssize_t w1_master_attribute_store_max_slave_count(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int tmp; + struct w1_master *md = dev_to_w1_master(dev); + + if (kstrtoint(buf, 0, &tmp) == -EINVAL || tmp < 1) + return -EINVAL; + + mutex_lock(&md->mutex); + md->max_slave_count = tmp; + /* allow each time the max_slave_count is updated */ + clear_bit(W1_WARN_MAX_COUNT, &md->flags); + mutex_unlock(&md->mutex); + + return count; +} + static ssize_t w1_master_attribute_show_max_slave_count(struct device *dev, struct device_attribute *attr, char *buf) { struct w1_master *md = dev_to_w1_master(dev); @@ -352,23 +373,20 @@ static ssize_t w1_master_attribute_show_slaves(struct device *dev, { struct w1_master *md = dev_to_w1_master(dev); int c = PAGE_SIZE; + struct list_head *ent, *n; + struct w1_slave *sl = NULL; - mutex_lock(&md->mutex); - - if (md->slave_count == 0) - c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n"); - else { - struct list_head *ent, *n; - struct w1_slave *sl; + mutex_lock(&md->list_mutex); - list_for_each_safe(ent, n, &md->slist) { - sl = list_entry(ent, struct w1_slave, w1_slave_entry); + list_for_each_safe(ent, n, &md->slist) { + sl = list_entry(ent, struct w1_slave, w1_slave_entry); - c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name); - } + c -= snprintf(buf + PAGE_SIZE - c, c, "%s\n", sl->name); } + if (!sl) + c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n"); - mutex_unlock(&md->mutex); + mutex_unlock(&md->list_mutex); return PAGE_SIZE - c; } @@ -422,19 +440,22 @@ static int w1_atoreg_num(struct device *dev, const char *buf, size_t count, } /* Searches the slaves in the w1_master and returns a pointer or NULL. - * Note: must hold the mutex + * Note: must not hold list_mutex */ -static struct w1_slave *w1_slave_search_device(struct w1_master *dev, +struct w1_slave *w1_slave_search_device(struct w1_master *dev, struct w1_reg_num *rn) { struct w1_slave *sl; + mutex_lock(&dev->list_mutex); list_for_each_entry(sl, &dev->slist, w1_slave_entry) { if (sl->reg_num.family == rn->family && sl->reg_num.id == rn->id && sl->reg_num.crc == rn->crc) { + mutex_unlock(&dev->list_mutex); return sl; } } + mutex_unlock(&dev->list_mutex); return NULL; } @@ -491,7 +512,10 @@ static ssize_t w1_master_attribute_store_remove(struct device *dev, mutex_lock(&md->mutex); sl = w1_slave_search_device(md, &rn); if (sl) { - w1_slave_detach(sl); + result = w1_slave_detach(sl); + /* refcnt 0 means it was detached in the call */ + if (result == 0) + result = count; } else { dev_info(dev, "Device %02x-%012llx doesn't exists\n", rn.family, (unsigned long long)rn.id); @@ -516,7 +540,7 @@ static ssize_t w1_master_attribute_store_remove(struct device *dev, static W1_MASTER_ATTR_RO(name, S_IRUGO); static W1_MASTER_ATTR_RO(slaves, S_IRUGO); static W1_MASTER_ATTR_RO(slave_count, S_IRUGO); -static W1_MASTER_ATTR_RO(max_slave_count, S_IRUGO); +static W1_MASTER_ATTR_RW(max_slave_count, S_IRUGO | S_IWUSR | S_IWGRP); static W1_MASTER_ATTR_RO(attempts, S_IRUGO); static W1_MASTER_ATTR_RO(timeout, S_IRUGO); static W1_MASTER_ATTR_RO(pointer, S_IRUGO); @@ -686,12 +710,14 @@ static int __w1_attach_slave_device(struct w1_slave *sl) dev_set_uevent_suppress(&sl->dev, false); kobject_uevent(&sl->dev.kobj, KOBJ_ADD); + mutex_lock(&sl->master->list_mutex); list_add_tail(&sl->w1_slave_entry, &sl->master->slist); + mutex_unlock(&sl->master->list_mutex); return 0; } -static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) +int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) { struct w1_slave *sl; struct w1_family *f; @@ -713,8 +739,8 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) memset(&msg, 0, sizeof(msg)); memcpy(&sl->reg_num, rn, sizeof(sl->reg_num)); - atomic_set(&sl->refcnt, 0); - init_completion(&sl->released); + atomic_set(&sl->refcnt, 1); + atomic_inc(&sl->master->refcnt); /* slave modules need to be loaded in a context with unlocked mutex */ mutex_unlock(&dev->mutex); @@ -754,23 +780,48 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) return 0; } -void w1_slave_detach(struct w1_slave *sl) +int w1_unref_slave(struct w1_slave *sl) { - struct w1_netlink_msg msg; - - dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, sl->name, sl); - - list_del(&sl->w1_slave_entry); - - memset(&msg, 0, sizeof(msg)); - memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); - msg.type = W1_SLAVE_REMOVE; - w1_netlink_send(sl->master, &msg); - - device_unregister(&sl->dev); + struct w1_master *dev = sl->master; + int refcnt; + mutex_lock(&dev->list_mutex); + refcnt = atomic_sub_return(1, &sl->refcnt); + if (refcnt == 0) { + struct w1_netlink_msg msg; + + dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, + sl->name, sl); + + list_del(&sl->w1_slave_entry); + + memset(&msg, 0, sizeof(msg)); + memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); + msg.type = W1_SLAVE_REMOVE; + w1_netlink_send(sl->master, &msg); + + device_unregister(&sl->dev); + #ifdef DEBUG + memset(sl, 0, sizeof(*sl)); + #endif + kfree(sl); + } + atomic_dec(&dev->refcnt); + mutex_unlock(&dev->list_mutex); + return refcnt; +} - wait_for_completion(&sl->released); - kfree(sl); +int w1_slave_detach(struct w1_slave *sl) +{ + /* Only detach a slave once as it decreases the refcnt each time. */ + int destroy_now; + mutex_lock(&sl->master->list_mutex); + destroy_now = !test_bit(W1_SLAVE_DETACH, &sl->flags); + set_bit(W1_SLAVE_DETACH, &sl->flags); + mutex_unlock(&sl->master->list_mutex); + + if (destroy_now) + destroy_now = !w1_unref_slave(sl); + return destroy_now ? 0 : -EBUSY; } struct w1_master *w1_search_master_id(u32 id) @@ -799,7 +850,7 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id) mutex_lock(&w1_mlock); list_for_each_entry(dev, &w1_masters, w1_master_entry) { - mutex_lock(&dev->mutex); + mutex_lock(&dev->list_mutex); list_for_each_entry(sl, &dev->slist, w1_slave_entry) { if (sl->reg_num.family == id->family && sl->reg_num.id == id->id && @@ -810,7 +861,7 @@ struct w1_slave *w1_search_slave(struct w1_reg_num *id) break; } } - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->list_mutex); if (found) break; @@ -830,6 +881,7 @@ void w1_reconnect_slaves(struct w1_family *f, int attach) dev_dbg(&dev->dev, "Reconnecting slaves in device %s " "for family %02x.\n", dev->name, f->fid); mutex_lock(&dev->mutex); + mutex_lock(&dev->list_mutex); list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { /* If it is a new family, slaves with the default * family driver and are that family will be @@ -841,14 +893,19 @@ void w1_reconnect_slaves(struct w1_family *f, int attach) (!attach && sl->family->fid == f->fid)) { struct w1_reg_num rn; + mutex_unlock(&dev->list_mutex); memcpy(&rn, &sl->reg_num, sizeof(rn)); - w1_slave_detach(sl); - - w1_attach_slave_device(dev, &rn); + /* If it was already in use let the automatic + * scan pick it up again later. + */ + if (!w1_slave_detach(sl)) + w1_attach_slave_device(dev, &rn); + mutex_lock(&dev->list_mutex); } } dev_dbg(&dev->dev, "Reconnecting slaves in device %s " "has been finished.\n", dev->name); + mutex_unlock(&dev->list_mutex); mutex_unlock(&dev->mutex); } mutex_unlock(&w1_mlock); @@ -876,7 +933,12 @@ void w1_slave_found(struct w1_master *dev, u64 rn) } /** - * Performs a ROM Search & registers any devices found. + * w1_search() - Performs a ROM Search & registers any devices found. + * @dev: The master device to search + * @search_type: W1_SEARCH to search all devices, or W1_ALARM_SEARCH + * to return only devices in the alarmed state + * @cb: Function to call when a device is found + * * The 1-wire search is a simple binary tree search. * For each bit of the address, we read two bits and write one bit. * The bit written will put to sleep all devies that don't match that bit. @@ -886,8 +948,6 @@ void w1_slave_found(struct w1_master *dev, u64 rn) * * See "Application note 187 1-wire search algorithm" at www.maxim-ic.com * - * @dev The master device to search - * @cb Function to call when a device is found */ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb) { @@ -898,7 +958,8 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb u8 triplet_ret = 0; search_bit = 0; - rn = last_rn = 0; + rn = dev->search_id; + last_rn = 0; last_device = 0; last_zero = -1; @@ -945,7 +1006,7 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb else search_bit = ((last_rn >> i) & 0x1); - /** Read two bits and write one bit */ + /* Read two bits and write one bit */ triplet_ret = w1_triplet(dev, search_bit); /* quit if no device responded */ @@ -960,8 +1021,7 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb tmp64 = (triplet_ret >> 2); rn |= (tmp64 << i); - /* ensure we're called from kthread and not by netlink callback */ - if (!dev->priv && kthread_should_stop()) { + if (test_bit(W1_ABORT_SEARCH, &dev->flags)) { mutex_unlock(&dev->bus_mutex); dev_dbg(&dev->dev, "Abort w1_search\n"); return; @@ -970,11 +1030,30 @@ void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb mutex_unlock(&dev->bus_mutex); if ( (triplet_ret & 0x03) != 0x03 ) { - if ( (desc_bit == last_zero) || (last_zero < 0)) + if ((desc_bit == last_zero) || (last_zero < 0)) { last_device = 1; + dev->search_id = 0; + } else { + dev->search_id = rn; + } desc_bit = last_zero; cb(dev, rn); } + + if (!last_device && slave_count == dev->max_slave_count && + !test_bit(W1_WARN_MAX_COUNT, &dev->flags)) { + /* Only max_slave_count will be scanned in a search, + * but it will start where it left off next search + * until all ids are identified and then it will start + * over. A continued search will report the previous + * last id as the first id (provided it is still on the + * bus). + */ + dev_info(&dev->dev, "%s: max_slave_count %d reached, " + "will continue next search.\n", __func__, + dev->max_slave_count); + set_bit(W1_WARN_MAX_COUNT, &dev->flags); + } } } @@ -983,17 +1062,24 @@ void w1_search_process_cb(struct w1_master *dev, u8 search_type, { struct w1_slave *sl, *sln; + mutex_lock(&dev->list_mutex); list_for_each_entry(sl, &dev->slist, w1_slave_entry) clear_bit(W1_SLAVE_ACTIVE, &sl->flags); + mutex_unlock(&dev->list_mutex); w1_search_devices(dev, search_type, cb); + mutex_lock(&dev->list_mutex); list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { - if (!test_bit(W1_SLAVE_ACTIVE, &sl->flags) && !--sl->ttl) + if (!test_bit(W1_SLAVE_ACTIVE, &sl->flags) && !--sl->ttl) { + mutex_unlock(&dev->list_mutex); w1_slave_detach(sl); + mutex_lock(&dev->list_mutex); + } else if (test_bit(W1_SLAVE_ACTIVE, &sl->flags)) sl->ttl = dev->slave_ttl; } + mutex_unlock(&dev->list_mutex); if (dev->search_count > 0) dev->search_count--; @@ -1004,6 +1090,32 @@ static void w1_search_process(struct w1_master *dev, u8 search_type) w1_search_process_cb(dev, search_type, w1_slave_found); } +/** + * w1_process_callbacks() - execute each dev->async_list callback entry + * @dev: w1_master device + * + * Return: 1 if there were commands to executed 0 otherwise + */ +int w1_process_callbacks(struct w1_master *dev) +{ + int ret = 0; + struct w1_async_cmd *async_cmd, *async_n; + + /* The list can be added to in another thread, loop until it is empty */ + while (!list_empty(&dev->async_list)) { + list_for_each_entry_safe(async_cmd, async_n, &dev->async_list, + async_entry) { + /* drop the lock, if it is a search it can take a long + * time */ + mutex_unlock(&dev->list_mutex); + async_cmd->cb(dev, async_cmd); + ret = 1; + mutex_lock(&dev->list_mutex); + } + } + return ret; +} + int w1_process(void *data) { struct w1_master *dev = (struct w1_master *) data; @@ -1011,23 +1123,46 @@ int w1_process(void *data) * time can be calculated in jiffies once. */ const unsigned long jtime = msecs_to_jiffies(w1_timeout * 1000); + /* remainder if it woke up early */ + unsigned long jremain = 0; - while (!kthread_should_stop()) { - if (dev->search_count) { + for (;;) { + + if (!jremain && dev->search_count) { mutex_lock(&dev->mutex); w1_search_process(dev, W1_SEARCH); mutex_unlock(&dev->mutex); } + mutex_lock(&dev->list_mutex); + /* Note, w1_process_callback drops the lock while processing, + * but locks it again before returning. + */ + if (!w1_process_callbacks(dev) && jremain) { + /* a wake up is either to stop the thread, process + * callbacks, or search, it isn't process callbacks, so + * schedule a search. + */ + jremain = 1; + } + try_to_freeze(); __set_current_state(TASK_INTERRUPTIBLE); + /* hold list_mutex until after interruptible to prevent loosing + * the wakeup signal when async_cmd is added. + */ + mutex_unlock(&dev->list_mutex); + if (kthread_should_stop()) break; /* Only sleep when the search is active. */ - if (dev->search_count) - schedule_timeout(jtime); + if (dev->search_count) { + if (!jremain) + jremain = jtime; + jremain = schedule_timeout(jremain); + } else schedule(); } diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index ca8081a101d6..734dab7fc687 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -22,6 +22,13 @@ #ifndef __W1_H #define __W1_H +/** + * struct w1_reg_num - broken out slave device id + * + * @family: identifies the type of device + * @id: along with family is the unique device id + * @crc: checksum of the other bytes + */ struct w1_reg_num { #if defined(__LITTLE_ENDIAN_BITFIELD) @@ -58,7 +65,24 @@ struct w1_reg_num #define W1_RESUME_CMD 0xA5 #define W1_SLAVE_ACTIVE 0 +#define W1_SLAVE_DETACH 1 +/** + * struct w1_slave - holds a single slave device on the bus + * + * @owner: Points to the one wire "wire" kernel module. + * @name: Device id is ascii. + * @w1_slave_entry: data for the linked list + * @reg_num: the slave id in binary + * @refcnt: reference count, delete when 0 + * @flags: bit flags for W1_SLAVE_ACTIVE W1_SLAVE_DETACH + * @ttl: decrement per search this slave isn't found, deatch at 0 + * @master: bus which this slave is on + * @family: module for device family type + * @family_data: pointer for use by the family module + * @dev: kernel device identifier + * + */ struct w1_slave { struct module *owner; @@ -66,7 +90,6 @@ struct w1_slave struct list_head w1_slave_entry; struct w1_reg_num reg_num; atomic_t refcnt; - u8 rom[9]; int ttl; unsigned long flags; @@ -74,99 +97,146 @@ struct w1_slave struct w1_family *family; void *family_data; struct device dev; - struct completion released; }; typedef void (*w1_slave_found_callback)(struct w1_master *, u64); /** + * struct w1_bus_master - operations available on a bus master + * + * @data: the first parameter in all the functions below + * + * @read_bit: Sample the line level @return the level read (0 or 1) + * + * @write_bit: Sets the line level + * + * @touch_bit: the lowest-level function for devices that really support the + * 1-wire protocol. + * touch_bit(0) = write-0 cycle + * touch_bit(1) = write-1 / read cycle + * @return the bit read (0 or 1) + * + * @read_byte: Reads a bytes. Same as 8 touch_bit(1) calls. + * @return the byte read + * + * @write_byte: Writes a byte. Same as 8 touch_bit(x) calls. + * + * @read_block: Same as a series of read_byte() calls + * @return the number of bytes read + * + * @write_block: Same as a series of write_byte() calls + * + * @triplet: Combines two reads and a smart write for ROM searches + * @return bit0=Id bit1=comp_id bit2=dir_taken + * + * @reset_bus: long write-0 with a read for the presence pulse detection + * @return -1=Error, 0=Device present, 1=No device present + * + * @set_pullup: Put out a strong pull-up pulse of the specified duration. + * @return -1=Error, 0=completed + * + * @search: Really nice hardware can handles the different types of ROM search + * w1_master* is passed to the slave found callback. + * u8 is search_type, W1_SEARCH or W1_ALARM_SEARCH + * * Note: read_bit and write_bit are very low level functions and should only * be used with hardware that doesn't really support 1-wire operations, * like a parallel/serial port. * Either define read_bit and write_bit OR define, at minimum, touch_bit and * reset_bus. + * */ struct w1_bus_master { - /** the first parameter in all the functions below */ void *data; - /** - * Sample the line level - * @return the level read (0 or 1) - */ u8 (*read_bit)(void *); - /** Sets the line level */ void (*write_bit)(void *, u8); - /** - * touch_bit is the lowest-level function for devices that really - * support the 1-wire protocol. - * touch_bit(0) = write-0 cycle - * touch_bit(1) = write-1 / read cycle - * @return the bit read (0 or 1) - */ u8 (*touch_bit)(void *, u8); - /** - * Reads a bytes. Same as 8 touch_bit(1) calls. - * @return the byte read - */ u8 (*read_byte)(void *); - /** - * Writes a byte. Same as 8 touch_bit(x) calls. - */ void (*write_byte)(void *, u8); - /** - * Same as a series of read_byte() calls - * @return the number of bytes read - */ u8 (*read_block)(void *, u8 *, int); - /** Same as a series of write_byte() calls */ void (*write_block)(void *, const u8 *, int); - /** - * Combines two reads and a smart write for ROM searches - * @return bit0=Id bit1=comp_id bit2=dir_taken - */ u8 (*triplet)(void *, u8); - /** - * long write-0 with a read for the presence pulse detection - * @return -1=Error, 0=Device present, 1=No device present - */ u8 (*reset_bus)(void *); - /** - * Put out a strong pull-up pulse of the specified duration. - * @return -1=Error, 0=completed - */ u8 (*set_pullup)(void *, int); - /** Really nice hardware can handles the different types of ROM search - * w1_master* is passed to the slave found callback. - */ void (*search)(void *, struct w1_master *, u8, w1_slave_found_callback); }; +/** + * enum w1_master_flags - bitfields used in w1_master.flags + * @W1_ABORT_SEARCH: abort searching early on shutdown + * @W1_WARN_MAX_COUNT: limit warning when the maximum count is reached + */ +enum w1_master_flags { + W1_ABORT_SEARCH = 0, + W1_WARN_MAX_COUNT = 1, +}; + +/** + * struct w1_master - one per bus master + * @w1_master_entry: master linked list + * @owner: module owner + * @name: dynamically allocate bus name + * @list_mutex: protect slist and async_list + * @slist: linked list of slaves + * @async_list: linked list of netlink commands to execute + * @max_slave_count: maximum number of slaves to search for at a time + * @slave_count: current number of slaves known + * @attempts: number of searches ran + * @slave_ttl: number of searches before a slave is timed out + * @initialized: prevent init/removal race conditions + * @id: w1 bus number + * @search_count: number of automatic searches to run, -1 unlimited + * @search_id: allows continuing a search + * @refcnt: reference count + * @priv: private data storage + * @priv_size: size allocated + * @enable_pullup: allows a strong pullup + * @pullup_duration: time for the next strong pullup + * @flags: one of w1_master_flags + * @thread: thread for bus search and netlink commands + * @mutex: protect most of w1_master + * @bus_mutex: pretect concurrent bus access + * @driver: sysfs driver + * @dev: sysfs device + * @bus_master: io operations available + * @seq: sequence number used for netlink broadcasts + * @portid: destination for the current netlink command + */ struct w1_master { struct list_head w1_master_entry; struct module *owner; unsigned char name[W1_MAXNAMELEN]; + /* list_mutex protects just slist and async_list so slaves can be + * searched for and async commands added while the master has + * w1_master.mutex locked and is operating on the bus. + * lock order w1_mlock, w1_master.mutex, w1_master.list_mutex + */ + struct mutex list_mutex; struct list_head slist; + struct list_head async_list; int max_slave_count, slave_count; unsigned long attempts; int slave_ttl; int initialized; u32 id; int search_count; + /* id to start searching on, to continue a search or 0 to restart */ + u64 search_id; atomic_t refcnt; @@ -178,6 +248,8 @@ struct w1_master /** 5V strong pullup duration in milliseconds, zero disabled. */ int pullup_duration; + long flags; + struct task_struct *thread; struct mutex mutex; struct mutex bus_mutex; @@ -188,16 +260,41 @@ struct w1_master struct w1_bus_master *bus_master; u32 seq; + /* port id to send netlink responses to. The value is temporarily + * stored here while processing a message, set after locking the + * mutex, zero before unlocking the mutex. + */ + u32 portid; +}; + +/** + * struct w1_async_cmd - execute callback from the w1_process kthread + * @async_entry: link entry + * @cb: callback function, must list_del and destroy this list before + * returning + * + * When inserted into the w1_master async_list, w1_process will execute + * the callback. Embed this into the structure with the command details. + */ +struct w1_async_cmd { + struct list_head async_entry; + void (*cb)(struct w1_master *dev, struct w1_async_cmd *async_cmd); }; int w1_create_master_attributes(struct w1_master *); void w1_destroy_master_attributes(struct w1_master *master); void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); +/* call w1_unref_slave to release the reference counts w1_search_slave added */ struct w1_slave *w1_search_slave(struct w1_reg_num *id); +/* decrements the reference on sl->master and sl, and cleans up if zero + * returns the reference count after it has been decremented */ +int w1_unref_slave(struct w1_slave *sl); void w1_slave_found(struct w1_master *dev, u64 rn); void w1_search_process_cb(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); +struct w1_slave *w1_slave_search_device(struct w1_master *dev, + struct w1_reg_num *rn); struct w1_master *w1_search_master_id(u32 id); /* Disconnect and reconnect devices in the given family. Used for finding @@ -206,7 +303,9 @@ struct w1_master *w1_search_master_id(u32 id); * has just been registered, to 0 when it has been unregistered. */ void w1_reconnect_slaves(struct w1_family *f, int attach); -void w1_slave_detach(struct w1_slave *sl); +int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn); +/* 0 success, otherwise EBUSY */ +int w1_slave_detach(struct w1_slave *sl); u8 w1_triplet(struct w1_master *dev, int bdir); void w1_write_8(struct w1_master *, u8); @@ -242,6 +341,7 @@ extern int w1_max_slave_ttl; extern struct list_head w1_masters; extern struct mutex w1_mlock; +extern int w1_process_callbacks(struct w1_master *dev); extern int w1_process(void *); #endif /* __KERNEL__ */ diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c index e9309778ee72..3bff6b37b472 100644 --- a/drivers/w1/w1_family.c +++ b/drivers/w1/w1_family.c @@ -31,6 +31,10 @@ DEFINE_SPINLOCK(w1_flock); static LIST_HEAD(w1_families); +/** + * w1_register_family() - register a device family driver + * @newf: family to register + */ int w1_register_family(struct w1_family *newf) { struct list_head *ent, *n; @@ -59,6 +63,10 @@ int w1_register_family(struct w1_family *newf) return ret; } +/** + * w1_unregister_family() - unregister a device family driver + * @fent: family to unregister + */ void w1_unregister_family(struct w1_family *fent) { struct list_head *ent, *n; diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 4ad0e81b6404..26ca1343055b 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -48,6 +48,12 @@ struct w1_slave; +/** + * struct w1_family_ops - operations for a family type + * @add_slave: add_slave + * @remove_slave: remove_slave + * @groups: sysfs group + */ struct w1_family_ops { int (* add_slave)(struct w1_slave *); @@ -55,6 +61,13 @@ struct w1_family_ops const struct attribute_group **groups; }; +/** + * struct w1_family - reference counted family structure. + * @family_entry: family linked list + * @fid: 8 bit family identifier + * @fops: operations for this family + * @refcnt: reference counter + */ struct w1_family { struct list_head family_entry; diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index 590bd8a7cd1b..9b084db739c7 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c @@ -75,8 +75,10 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, atomic_set(&dev->refcnt, 2); INIT_LIST_HEAD(&dev->slist); + INIT_LIST_HEAD(&dev->async_list); mutex_init(&dev->mutex); mutex_init(&dev->bus_mutex); + mutex_init(&dev->list_mutex); memcpy(&dev->dev, device, sizeof(struct device)); dev_set_name(&dev->dev, "w1_bus_master%u", dev->id); @@ -103,6 +105,10 @@ static void w1_free_dev(struct w1_master *dev) device_unregister(&dev->dev); } +/** + * w1_add_master_device() - registers a new master device + * @master: master bus device to register + */ int w1_add_master_device(struct w1_bus_master *master) { struct w1_master *dev, *entry; @@ -172,6 +178,7 @@ int w1_add_master_device(struct w1_bus_master *master) #if 0 /* Thread cleanup code, not required currently. */ err_out_kill_thread: + set_bit(W1_ABORT_SEARCH, &dev->flags); kthread_stop(dev->thread); #endif err_out_rm_attr: @@ -187,16 +194,22 @@ void __w1_remove_master_device(struct w1_master *dev) struct w1_netlink_msg msg; struct w1_slave *sl, *sln; - kthread_stop(dev->thread); - mutex_lock(&w1_mlock); list_del(&dev->w1_master_entry); mutex_unlock(&w1_mlock); + set_bit(W1_ABORT_SEARCH, &dev->flags); + kthread_stop(dev->thread); + mutex_lock(&dev->mutex); - list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) + mutex_lock(&dev->list_mutex); + list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { + mutex_unlock(&dev->list_mutex); w1_slave_detach(sl); + mutex_lock(&dev->list_mutex); + } w1_destroy_master_attributes(dev); + mutex_unlock(&dev->list_mutex); mutex_unlock(&dev->mutex); atomic_dec(&dev->refcnt); @@ -206,7 +219,9 @@ void __w1_remove_master_device(struct w1_master *dev) if (msleep_interruptible(1000)) flush_signals(current); + w1_process_callbacks(dev); } + w1_process_callbacks(dev); memset(&msg, 0, sizeof(msg)); msg.id.mst.id = dev->id; @@ -216,6 +231,10 @@ void __w1_remove_master_device(struct w1_master *dev) w1_free_dev(dev); } +/** + * w1_remove_master_device() - unregister a master device + * @bm: master bus device to remove + */ void w1_remove_master_device(struct w1_bus_master *bm) { struct w1_master *dev, *found = NULL; diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index e10acc237733..282092421cc9 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -62,7 +62,9 @@ static void w1_write_bit(struct w1_master *dev, int bit); static u8 w1_read_bit(struct w1_master *dev); /** - * Generates a write-0 or write-1 cycle and samples the level. + * w1_touch_bit() - Generates a write-0 or write-1 cycle and samples the level. + * @dev: the master device + * @bit: 0 - write a 0, 1 - write a 0 read the level */ static u8 w1_touch_bit(struct w1_master *dev, int bit) { @@ -77,7 +79,10 @@ static u8 w1_touch_bit(struct w1_master *dev, int bit) } /** - * Generates a write-0 or write-1 cycle. + * w1_write_bit() - Generates a write-0 or write-1 cycle. + * @dev: the master device + * @bit: bit to write + * * Only call if dev->bus_master->touch_bit is NULL */ static void w1_write_bit(struct w1_master *dev, int bit) @@ -102,11 +107,12 @@ static void w1_write_bit(struct w1_master *dev, int bit) } /** + * w1_pre_write() - pre-write operations + * @dev: the master device + * * Pre-write operation, currently only supporting strong pullups. * Program the hardware for a strong pullup, if one has been requested and * the hardware supports it. - * - * @param dev the master device */ static void w1_pre_write(struct w1_master *dev) { @@ -118,11 +124,12 @@ static void w1_pre_write(struct w1_master *dev) } /** + * w1_post_write() - post-write options + * @dev: the master device + * * Post-write operation, currently only supporting strong pullups. * If a strong pullup was requested, clear it if the hardware supports * them, or execute the delay otherwise, in either case clear the request. - * - * @param dev the master device */ static void w1_post_write(struct w1_master *dev) { @@ -136,10 +143,9 @@ static void w1_post_write(struct w1_master *dev) } /** - * Writes 8 bits. - * - * @param dev the master device - * @param byte the byte to write + * w1_write_8() - Writes 8 bits. + * @dev: the master device + * @byte: the byte to write */ void w1_write_8(struct w1_master *dev, u8 byte) { @@ -161,7 +167,9 @@ EXPORT_SYMBOL_GPL(w1_write_8); /** - * Generates a write-1 cycle and samples the level. + * w1_read_bit() - Generates a write-1 cycle and samples the level. + * @dev: the master device + * * Only call if dev->bus_master->touch_bit is NULL */ static u8 w1_read_bit(struct w1_master *dev) @@ -185,16 +193,17 @@ static u8 w1_read_bit(struct w1_master *dev) } /** - * Does a triplet - used for searching ROM addresses. + * w1_triplet() - * Does a triplet - used for searching ROM addresses. + * @dev: the master device + * @bdir: the bit to write if both id_bit and comp_bit are 0 + * * Return bits: * bit 0 = id_bit * bit 1 = comp_bit * bit 2 = dir_taken * If both bits 0 & 1 are set, the search should be restarted. * - * @param dev the master device - * @param bdir the bit to write if both id_bit and comp_bit are 0 - * @return bit fields - see above + * Return: bit fields - see above */ u8 w1_triplet(struct w1_master *dev, int bdir) { @@ -226,10 +235,10 @@ u8 w1_triplet(struct w1_master *dev, int bdir) } /** - * Reads 8 bits. + * w1_read_8() - Reads 8 bits. + * @dev: the master device * - * @param dev the master device - * @return the byte read + * Return: the byte read */ u8 w1_read_8(struct w1_master *dev) { @@ -247,11 +256,10 @@ u8 w1_read_8(struct w1_master *dev) EXPORT_SYMBOL_GPL(w1_read_8); /** - * Writes a series of bytes. - * - * @param dev the master device - * @param buf pointer to the data to write - * @param len the number of bytes to write + * w1_write_block() - Writes a series of bytes. + * @dev: the master device + * @buf: pointer to the data to write + * @len: the number of bytes to write */ void w1_write_block(struct w1_master *dev, const u8 *buf, int len) { @@ -269,11 +277,10 @@ void w1_write_block(struct w1_master *dev, const u8 *buf, int len) EXPORT_SYMBOL_GPL(w1_write_block); /** - * Touches a series of bytes. - * - * @param dev the master device - * @param buf pointer to the data to write - * @param len the number of bytes to write + * w1_touch_block() - Touches a series of bytes. + * @dev: the master device + * @buf: pointer to the data to write + * @len: the number of bytes to write */ void w1_touch_block(struct w1_master *dev, u8 *buf, int len) { @@ -294,12 +301,11 @@ void w1_touch_block(struct w1_master *dev, u8 *buf, int len) EXPORT_SYMBOL_GPL(w1_touch_block); /** - * Reads a series of bytes. - * - * @param dev the master device - * @param buf pointer to the buffer to fill - * @param len the number of bytes to read - * @return the number of bytes read + * w1_read_block() - Reads a series of bytes. + * @dev: the master device + * @buf: pointer to the buffer to fill + * @len: the number of bytes to read + * Return: the number of bytes read */ u8 w1_read_block(struct w1_master *dev, u8 *buf, int len) { @@ -319,10 +325,9 @@ u8 w1_read_block(struct w1_master *dev, u8 *buf, int len) EXPORT_SYMBOL_GPL(w1_read_block); /** - * Issues a reset bus sequence. - * - * @param dev The bus master pointer - * @return 0=Device present, 1=No device present or error + * w1_reset_bus() - Issues a reset bus sequence. + * @dev: the master device + * Return: 0=Device present, 1=No device present or error */ int w1_reset_bus(struct w1_master *dev) { @@ -383,12 +388,15 @@ void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_cal } /** + * w1_reset_select_slave() - reset and select a slave + * @sl: the slave to select + * * Resets the bus and then selects the slave by sending either a skip rom - * or a rom match. + * or a rom match. A skip rom is issued if there is only one device + * registered on the bus. * The w1 master lock must be held. * - * @param sl the slave to select - * @return 0=success, anything else=error + * Return: 0=success, anything else=error */ int w1_reset_select_slave(struct w1_slave *sl) { @@ -409,6 +417,9 @@ int w1_reset_select_slave(struct w1_slave *sl) EXPORT_SYMBOL_GPL(w1_reset_select_slave); /** + * w1_reset_resume_command() - resume instead of another match ROM + * @dev: the master device + * * When the workflow with a slave amongst many requires several * successive commands a reset between each, this function is similar * to doing a reset then a match ROM for the last matched ROM. The @@ -420,8 +431,6 @@ EXPORT_SYMBOL_GPL(w1_reset_select_slave); * doesn't work of course, but the resume command is the next best thing. * * The w1 master lock must be held. - * - * @param dev the master device */ int w1_reset_resume_command(struct w1_master *dev) { @@ -435,6 +444,10 @@ int w1_reset_resume_command(struct w1_master *dev) EXPORT_SYMBOL_GPL(w1_reset_resume_command); /** + * w1_next_pullup() - register for a strong pullup + * @dev: the master device + * @delay: time in milliseconds + * * Put out a strong pull-up of the specified duration after the next write * operation. Not all hardware supports strong pullups. Hardware that * doesn't support strong pullups will sleep for the given time after the @@ -442,8 +455,7 @@ EXPORT_SYMBOL_GPL(w1_reset_resume_command); * the next write, specifying zero will clear a previous request. * The w1 master lock must be held. * - * @param delay time in milliseconds - * @return 0=success, anything else=error + * Return: 0=success, anything else=error */ void w1_next_pullup(struct w1_master *dev, int delay) { diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 40788c925d1c..5234964fe001 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c @@ -45,7 +45,7 @@ void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) memcpy(w, msg, sizeof(struct w1_netlink_msg)); - cn_netlink_send(m, 0, GFP_KERNEL); + cn_netlink_send(m, dev->portid, 0, GFP_KERNEL); } static void w1_send_slave(struct w1_master *dev, u64 rn) @@ -54,53 +54,95 @@ static void w1_send_slave(struct w1_master *dev, u64 rn) struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1); struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); int avail; - - /* update kernel slave list */ - w1_slave_found(dev, rn); + u64 *data; avail = dev->priv_size - cmd->len; - if (avail > 8) { - u64 *data = (void *)(cmd + 1) + cmd->len; + if (avail < 8) { + msg->ack++; + cn_netlink_send(msg, dev->portid, 0, GFP_KERNEL); - *data = rn; - cmd->len += 8; - hdr->len += 8; - msg->len += 8; - return; + msg->len = sizeof(struct w1_netlink_msg) + + sizeof(struct w1_netlink_cmd); + hdr->len = sizeof(struct w1_netlink_cmd); + cmd->len = 0; } - msg->ack++; - cn_netlink_send(msg, 0, GFP_KERNEL); + data = (void *)(cmd + 1) + cmd->len; - msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd); - hdr->len = sizeof(struct w1_netlink_cmd); - cmd->len = 0; + *data = rn; + cmd->len += 8; + hdr->len += 8; + msg->len += 8; } -static int w1_process_search_command(struct w1_master *dev, struct cn_msg *msg, - unsigned int avail) +static void w1_found_send_slave(struct w1_master *dev, u64 rn) { - struct w1_netlink_msg *hdr = (struct w1_netlink_msg *)(msg + 1); - struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)(hdr + 1); - int search_type = (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH; + /* update kernel slave list */ + w1_slave_found(dev, rn); - dev->priv = msg; - dev->priv_size = avail; + w1_send_slave(dev, rn); +} + +/* Get the current slave list, or search (with or without alarm) */ +static int w1_get_slaves(struct w1_master *dev, + struct cn_msg *req_msg, struct w1_netlink_msg *req_hdr, + struct w1_netlink_cmd *req_cmd) +{ + struct cn_msg *msg; + struct w1_netlink_msg *hdr; + struct w1_netlink_cmd *cmd; + struct w1_slave *sl; + + msg = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->id = req_msg->id; + msg->seq = req_msg->seq; + msg->ack = 0; + msg->len = sizeof(struct w1_netlink_msg) + + sizeof(struct w1_netlink_cmd); + + hdr = (struct w1_netlink_msg *)(msg + 1); + cmd = (struct w1_netlink_cmd *)(hdr + 1); + + hdr->type = W1_MASTER_CMD; + hdr->id = req_hdr->id; + hdr->len = sizeof(struct w1_netlink_cmd); + + cmd->cmd = req_cmd->cmd; + cmd->len = 0; - w1_search_process_cb(dev, search_type, w1_send_slave); + dev->priv = msg; + dev->priv_size = PAGE_SIZE - msg->len - sizeof(struct cn_msg); + + if (req_cmd->cmd == W1_CMD_LIST_SLAVES) { + __u64 rn; + mutex_lock(&dev->list_mutex); + list_for_each_entry(sl, &dev->slist, w1_slave_entry) { + memcpy(&rn, &sl->reg_num, sizeof(rn)); + w1_send_slave(dev, rn); + } + mutex_unlock(&dev->list_mutex); + } else { + w1_search_process_cb(dev, cmd->cmd == W1_CMD_ALARM_SEARCH ? + W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave); + } msg->ack = 0; - cn_netlink_send(msg, 0, GFP_KERNEL); + cn_netlink_send(msg, dev->portid, 0, GFP_KERNEL); dev->priv = NULL; dev->priv_size = 0; + kfree(msg); + return 0; } static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr, - struct w1_netlink_cmd *cmd) + struct w1_netlink_cmd *cmd, u32 portid) { void *data; struct w1_netlink_msg *h; @@ -131,7 +173,7 @@ static int w1_send_read_reply(struct cn_msg *msg, struct w1_netlink_msg *hdr, memcpy(c->data, cmd->data, c->len); - err = cn_netlink_send(cm, 0, GFP_KERNEL); + err = cn_netlink_send(cm, portid, 0, GFP_KERNEL); kfree(data); @@ -146,11 +188,11 @@ static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg, switch (cmd->cmd) { case W1_CMD_TOUCH: w1_touch_block(dev, cmd->data, cmd->len); - w1_send_read_reply(msg, hdr, cmd); + w1_send_read_reply(msg, hdr, cmd, dev->portid); break; case W1_CMD_READ: w1_read_block(dev, cmd->data, cmd->len); - w1_send_read_reply(msg, hdr, cmd); + w1_send_read_reply(msg, hdr, cmd, dev->portid); break; case W1_CMD_WRITE: w1_write_block(dev, cmd->data, cmd->len); @@ -163,38 +205,57 @@ static int w1_process_command_io(struct w1_master *dev, struct cn_msg *msg, return err; } -static int w1_process_command_master(struct w1_master *dev, struct cn_msg *req_msg, - struct w1_netlink_msg *req_hdr, struct w1_netlink_cmd *req_cmd) +static int w1_process_command_addremove(struct w1_master *dev, + struct cn_msg *msg, struct w1_netlink_msg *hdr, + struct w1_netlink_cmd *cmd) { - int err = -EINVAL; - struct cn_msg *msg; - struct w1_netlink_msg *hdr; - struct w1_netlink_cmd *cmd; + struct w1_slave *sl; + int err = 0; + struct w1_reg_num *id; - msg = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; + if (cmd->len != 8) + return -EINVAL; - msg->id = req_msg->id; - msg->seq = req_msg->seq; - msg->ack = 0; - msg->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd); + id = (struct w1_reg_num *)cmd->data; - hdr = (struct w1_netlink_msg *)(msg + 1); - cmd = (struct w1_netlink_cmd *)(hdr + 1); + sl = w1_slave_search_device(dev, id); + switch (cmd->cmd) { + case W1_CMD_SLAVE_ADD: + if (sl) + err = -EINVAL; + else + err = w1_attach_slave_device(dev, id); + break; + case W1_CMD_SLAVE_REMOVE: + if (sl) + w1_slave_detach(sl); + else + err = -EINVAL; + break; + default: + err = -EINVAL; + break; + } - hdr->type = W1_MASTER_CMD; - hdr->id = req_hdr->id; - hdr->len = sizeof(struct w1_netlink_cmd); + return err; +} - cmd->cmd = req_cmd->cmd; - cmd->len = 0; +static int w1_process_command_master(struct w1_master *dev, + struct cn_msg *req_msg, struct w1_netlink_msg *req_hdr, + struct w1_netlink_cmd *req_cmd) +{ + int err = -EINVAL; - switch (cmd->cmd) { + /* drop bus_mutex for search (does it's own locking), and add/remove + * which doesn't use the bus + */ + switch (req_cmd->cmd) { case W1_CMD_SEARCH: case W1_CMD_ALARM_SEARCH: - err = w1_process_search_command(dev, msg, - PAGE_SIZE - msg->len - sizeof(struct cn_msg)); + case W1_CMD_LIST_SLAVES: + mutex_unlock(&dev->bus_mutex); + err = w1_get_slaves(dev, req_msg, req_hdr, req_cmd); + mutex_lock(&dev->bus_mutex); break; case W1_CMD_READ: case W1_CMD_WRITE: @@ -204,12 +265,20 @@ static int w1_process_command_master(struct w1_master *dev, struct cn_msg *req_m case W1_CMD_RESET: err = w1_reset_bus(dev); break; + case W1_CMD_SLAVE_ADD: + case W1_CMD_SLAVE_REMOVE: + mutex_unlock(&dev->bus_mutex); + mutex_lock(&dev->mutex); + err = w1_process_command_addremove(dev, req_msg, req_hdr, + req_cmd); + mutex_unlock(&dev->mutex); + mutex_lock(&dev->bus_mutex); + break; default: err = -EINVAL; break; } - kfree(msg); return err; } @@ -223,7 +292,8 @@ static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg, return w1_process_command_io(sl->master, msg, hdr, cmd); } -static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mcmd) +static int w1_process_command_root(struct cn_msg *msg, + struct w1_netlink_msg *mcmd, u32 portid) { struct w1_master *m; struct cn_msg *cn; @@ -256,7 +326,7 @@ static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mc mutex_lock(&w1_mlock); list_for_each_entry(m, &w1_masters, w1_master_entry) { if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) { - cn_netlink_send(cn, 0, GFP_KERNEL); + cn_netlink_send(cn, portid, 0, GFP_KERNEL); cn->ack++; cn->len = sizeof(struct w1_netlink_msg); w->len = 0; @@ -269,7 +339,7 @@ static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mc id++; } cn->ack = 0; - cn_netlink_send(cn, 0, GFP_KERNEL); + cn_netlink_send(cn, portid, 0, GFP_KERNEL); mutex_unlock(&w1_mlock); kfree(cn); @@ -277,7 +347,7 @@ static int w1_process_command_root(struct cn_msg *msg, struct w1_netlink_msg *mc } static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rmsg, - struct w1_netlink_cmd *rcmd, int error) + struct w1_netlink_cmd *rcmd, int portid, int error) { struct cn_msg *cmsg; struct w1_netlink_msg *msg; @@ -304,35 +374,147 @@ static int w1_netlink_send_error(struct cn_msg *rcmsg, struct w1_netlink_msg *rm cmsg->len += sizeof(*cmd); } - error = cn_netlink_send(cmsg, 0, GFP_KERNEL); + error = cn_netlink_send(cmsg, portid, 0, GFP_KERNEL); kfree(cmsg); return error; } +/* Bundle together a reference count, the full message, and broken out + * commands to be executed on each w1 master kthread in one memory allocation. + */ +struct w1_cb_block { + atomic_t refcnt; + u32 portid; /* Sending process port ID */ + struct cn_msg msg; + /* cn_msg data */ + /* one or more variable length struct w1_cb_node */ +}; +struct w1_cb_node { + struct w1_async_cmd async; + /* pointers within w1_cb_block and msg data */ + struct w1_cb_block *block; + struct w1_netlink_msg *m; + struct w1_slave *sl; + struct w1_master *dev; +}; + +static void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd) +{ + struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node, + async); + u16 mlen = node->m->len; + u8 *cmd_data = node->m->data; + int err = 0; + struct w1_slave *sl = node->sl; + struct w1_netlink_cmd *cmd = NULL; + + mutex_lock(&dev->bus_mutex); + dev->portid = node->block->portid; + if (sl && w1_reset_select_slave(sl)) + err = -ENODEV; + + while (mlen && !err) { + cmd = (struct w1_netlink_cmd *)cmd_data; + + if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) { + err = -E2BIG; + break; + } + + if (sl) + err = w1_process_command_slave(sl, &node->block->msg, + node->m, cmd); + else + err = w1_process_command_master(dev, &node->block->msg, + node->m, cmd); + + w1_netlink_send_error(&node->block->msg, node->m, cmd, + node->block->portid, err); + err = 0; + + cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); + mlen -= cmd->len + sizeof(struct w1_netlink_cmd); + } + + if (!cmd || err) + w1_netlink_send_error(&node->block->msg, node->m, cmd, + node->block->portid, err); + + if (sl) + w1_unref_slave(sl); + else + atomic_dec(&dev->refcnt); + dev->portid = 0; + mutex_unlock(&dev->bus_mutex); + + mutex_lock(&dev->list_mutex); + list_del(&async_cmd->async_entry); + mutex_unlock(&dev->list_mutex); + + if (atomic_sub_return(1, &node->block->refcnt) == 0) + kfree(node->block); +} + static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1); - struct w1_netlink_cmd *cmd; struct w1_slave *sl; struct w1_master *dev; + u16 msg_len; int err = 0; + struct w1_cb_block *block = NULL; + struct w1_cb_node *node = NULL; + int node_count = 0; + + /* Count the number of master or slave commands there are to allocate + * space for one cb_node each. + */ + msg_len = msg->len; + while (msg_len && !err) { + if (m->len + sizeof(struct w1_netlink_msg) > msg_len) { + err = -E2BIG; + break; + } + + if (m->type == W1_MASTER_CMD || m->type == W1_SLAVE_CMD) + ++node_count; - while (msg->len && !err) { + msg_len -= sizeof(struct w1_netlink_msg) + m->len; + m = (struct w1_netlink_msg *)(((u8 *)m) + + sizeof(struct w1_netlink_msg) + m->len); + } + m = (struct w1_netlink_msg *)(msg + 1); + if (node_count) { + /* msg->len doesn't include itself */ + long size = sizeof(struct w1_cb_block) + msg->len + + node_count*sizeof(struct w1_cb_node); + block = kmalloc(size, GFP_KERNEL); + if (!block) { + w1_netlink_send_error(msg, m, NULL, nsp->portid, + -ENOMEM); + return; + } + atomic_set(&block->refcnt, 1); + block->portid = nsp->portid; + memcpy(&block->msg, msg, sizeof(*msg) + msg->len); + node = (struct w1_cb_node *)((u8 *)block->msg.data + msg->len); + } + + msg_len = msg->len; + while (msg_len && !err) { struct w1_reg_num id; u16 mlen = m->len; - u8 *cmd_data = m->data; dev = NULL; sl = NULL; - cmd = NULL; memcpy(&id, m->id.id, sizeof(id)); #if 0 printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n", __func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len); #endif - if (m->len + sizeof(struct w1_netlink_msg) > msg->len) { + if (m->len + sizeof(struct w1_netlink_msg) > msg_len) { err = -E2BIG; break; } @@ -344,7 +526,7 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) if (sl) dev = sl->master; } else { - err = w1_process_command_root(msg, m); + err = w1_process_command_root(msg, m, nsp->portid); goto out_cont; } @@ -357,41 +539,24 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) if (!mlen) goto out_cont; - mutex_lock(&dev->mutex); - - if (sl && w1_reset_select_slave(sl)) { - err = -ENODEV; - goto out_up; - } + atomic_inc(&block->refcnt); + node->async.cb = w1_process_cb; + node->block = block; + node->m = (struct w1_netlink_msg *)((u8 *)&block->msg + + (size_t)((u8 *)m - (u8 *)msg)); + node->sl = sl; + node->dev = dev; - while (mlen) { - cmd = (struct w1_netlink_cmd *)cmd_data; + mutex_lock(&dev->list_mutex); + list_add_tail(&node->async.async_entry, &dev->async_list); + wake_up_process(dev->thread); + mutex_unlock(&dev->list_mutex); + ++node; - if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) { - err = -E2BIG; - break; - } - - if (sl) - err = w1_process_command_slave(sl, msg, m, cmd); - else - err = w1_process_command_master(dev, msg, m, cmd); - - w1_netlink_send_error(msg, m, cmd, err); - err = 0; - - cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); - mlen -= cmd->len + sizeof(struct w1_netlink_cmd); - } -out_up: - atomic_dec(&dev->refcnt); - if (sl) - atomic_dec(&sl->refcnt); - mutex_unlock(&dev->mutex); out_cont: - if (!cmd || err) - w1_netlink_send_error(msg, m, cmd, err); - msg->len -= sizeof(struct w1_netlink_msg) + m->len; + if (err) + w1_netlink_send_error(msg, m, NULL, nsp->portid, err); + msg_len -= sizeof(struct w1_netlink_msg) + m->len; m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); /* @@ -400,6 +565,8 @@ out_cont: if (err == -ENODEV) err = 0; } + if (block && atomic_sub_return(1, &block->refcnt) == 0) + kfree(block); } int w1_init_netlink(void) diff --git a/drivers/w1/w1_netlink.h b/drivers/w1/w1_netlink.h index b0922dc29658..1e9504e67650 100644 --- a/drivers/w1/w1_netlink.h +++ b/drivers/w1/w1_netlink.h @@ -27,6 +27,18 @@ #include "w1.h" +/** + * enum w1_netlink_message_types - message type + * + * @W1_SLAVE_ADD: notification that a slave device was added + * @W1_SLAVE_REMOVE: notification that a slave device was removed + * @W1_MASTER_ADD: notification that a new bus master was added + * @W1_MASTER_REMOVE: notification that a bus masterwas removed + * @W1_MASTER_CMD: initiate operations on a specific master + * @W1_SLAVE_CMD: sends reset, selects the slave, then does a read/write/touch + * operation + * @W1_LIST_MASTERS: used to determine the bus master identifiers + */ enum w1_netlink_message_types { W1_SLAVE_ADD = 0, W1_SLAVE_REMOVE, @@ -52,6 +64,22 @@ struct w1_netlink_msg __u8 data[0]; }; +/** + * enum w1_commands - commands available for master or slave operations + * @W1_CMD_READ: read len bytes + * @W1_CMD_WRITE: write len bytes + * @W1_CMD_SEARCH: initiate a standard search, returns only the slave + * devices found during that search + * @W1_CMD_ALARM_SEARCH: search for devices that are currently alarming + * @W1_CMD_TOUCH: Touches a series of bytes. + * @W1_CMD_RESET: sends a bus reset on the given master + * @W1_CMD_SLAVE_ADD: adds a slave to the given master, + * 8 byte slave id at data[0] + * @W1_CMD_SLAVE_REMOVE: removes a slave to the given master, + * 8 byte slave id at data[0] + * @W1_CMD_LIST_SLAVES: list of slaves registered on this master + * @W1_CMD_MAX: number of available commands + */ enum w1_commands { W1_CMD_READ = 0, W1_CMD_WRITE, @@ -59,7 +87,10 @@ enum w1_commands { W1_CMD_ALARM_SEARCH, W1_CMD_TOUCH, W1_CMD_RESET, - W1_CMD_MAX, + W1_CMD_SLAVE_ADD, + W1_CMD_SLAVE_REMOVE, + W1_CMD_LIST_SLAVES, + W1_CMD_MAX }; struct w1_netlink_cmd diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 79d25894343a..0c6048d5c9a3 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -111,6 +111,15 @@ config WM8350_WATCHDOG Support for the watchdog in the WM8350 AudioPlus PMIC. When the watchdog triggers the system will be reset. +config XILINX_WATCHDOG + tristate "Xilinx Watchdog timer" + select WATCHDOG_CORE + help + Watchdog driver for the xps_timebase_wdt ip core. + + To compile this driver as a module, choose M here: the + module will be called of_xilinx_wdt. + # ALPHA Architecture # ARM Architecture @@ -292,7 +301,7 @@ config DAVINCI_WATCHDOG config ORION_WATCHDOG tristate "Orion watchdog" - depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE + depends on ARCH_ORION5X || ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer @@ -421,6 +430,17 @@ config SIRFSOC_WATCHDOG Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When the watchdog triggers the system will be reset. +config TEGRA_WATCHDOG + tristate "Tegra watchdog" + depends on ARCH_TEGRA || COMPILE_TEST + select WATCHDOG_CORE + help + Say Y here to include support for the watchdog timer + embedded in NVIDIA Tegra SoCs. + + To compile this driver as a module, choose M here: the + module will be called tegra_wdt. + # AVR32 Architecture config AT32AP700X_WDT @@ -533,7 +553,7 @@ config GEODE_WDT config SC520_WDT tristate "AMD Elan SC520 processor Watchdog" - depends on X86 + depends on MELAN help This is the driver for the hardware watchdog built in to the AMD "Elan" SC520 microcomputer commonly used in embedded systems. @@ -1023,18 +1043,6 @@ config M54xx_WATCHDOG # MicroBlaze Architecture -config XILINX_WATCHDOG - tristate "Xilinx Watchdog timer" - depends on MICROBLAZE - ---help--- - Watchdog driver for the xps_timebase_wdt ip core. - - IMPORTANT: The xps_timebase_wdt parent must have the property - "clock-frequency" at device tree. - - To compile this driver as a module, choose M here: the - module will be called of_xilinx_wdt. - # MIPS Architecture config ATH79_WDT @@ -1160,7 +1168,7 @@ config BCM2835_WDT config BCM_KONA_WDT tristate "BCM Kona Watchdog" - depends on ARCH_BCM + depends on ARCH_BCM_MOBILE select WATCHDOG_CORE help Support for the watchdog timer on the following Broadcom BCM281xx diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 985a66cda76f..1b5f3d5efad5 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o +obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/acquirewdt.c b/drivers/watchdog/acquirewdt.c index 5cf1621def9c..5614416f1032 100644 --- a/drivers/watchdog/acquirewdt.c +++ b/drivers/watchdog/acquirewdt.c @@ -239,7 +239,7 @@ static struct miscdevice acq_miscdev = { * Init & exit routines */ -static int acq_probe(struct platform_device *dev) +static int __init acq_probe(struct platform_device *dev) { int ret; @@ -291,7 +291,6 @@ static void acq_shutdown(struct platform_device *dev) } static struct platform_driver acquirewdt_driver = { - .probe = acq_probe, .remove = acq_remove, .shutdown = acq_shutdown, .driver = { @@ -306,20 +305,18 @@ static int __init acq_init(void) pr_info("WDT driver for Acquire single board computer initialising\n"); - err = platform_driver_register(&acquirewdt_driver); - if (err) - return err; - acq_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); - if (IS_ERR(acq_platform_device)) { - err = PTR_ERR(acq_platform_device); - goto unreg_platform_driver; - } + if (IS_ERR(acq_platform_device)) + return PTR_ERR(acq_platform_device); + + err = platform_driver_probe(&acquirewdt_driver, acq_probe); + if (err) + goto unreg_platform_device; return 0; -unreg_platform_driver: - platform_driver_unregister(&acquirewdt_driver); +unreg_platform_device: + platform_device_unregister(acq_platform_device); return err; } diff --git a/drivers/watchdog/advantechwdt.c b/drivers/watchdog/advantechwdt.c index a8961addc59c..7796db7fa6e1 100644 --- a/drivers/watchdog/advantechwdt.c +++ b/drivers/watchdog/advantechwdt.c @@ -238,7 +238,7 @@ static struct miscdevice advwdt_miscdev = { * Init & exit routines */ -static int advwdt_probe(struct platform_device *dev) +static int __init advwdt_probe(struct platform_device *dev) { int ret; @@ -299,7 +299,6 @@ static void advwdt_shutdown(struct platform_device *dev) } static struct platform_driver advwdt_driver = { - .probe = advwdt_probe, .remove = advwdt_remove, .shutdown = advwdt_shutdown, .driver = { @@ -314,21 +313,19 @@ static int __init advwdt_init(void) pr_info("WDT driver for Advantech single board computer initialising\n"); - err = platform_driver_register(&advwdt_driver); - if (err) - return err; - advwdt_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); - if (IS_ERR(advwdt_platform_device)) { - err = PTR_ERR(advwdt_platform_device); - goto unreg_platform_driver; - } + if (IS_ERR(advwdt_platform_device)) + return PTR_ERR(advwdt_platform_device); + + err = platform_driver_probe(&advwdt_driver, advwdt_probe); + if (err) + goto unreg_platform_device; return 0; -unreg_platform_driver: - platform_driver_unregister(&advwdt_driver); +unreg_platform_device: + platform_device_unregister(advwdt_platform_device); return err; } diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c index 3a996576343a..ae6c287a49cb 100644 --- a/drivers/watchdog/ar7_wdt.c +++ b/drivers/watchdog/ar7_wdt.c @@ -28,7 +28,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/miscdevice.h> #include <linux/platform_device.h> #include <linux/watchdog.h> diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c index afe7d17e6776..25b5c67d3af9 100644 --- a/drivers/watchdog/at32ap700x_wdt.c +++ b/drivers/watchdog/at32ap700x_wdt.c @@ -323,10 +323,8 @@ static int __init at32_wdt_probe(struct platform_device *pdev) wdt = devm_kzalloc(&pdev->dev, sizeof(struct wdt_at32ap700x), GFP_KERNEL); - if (!wdt) { - dev_dbg(&pdev->dev, "no memory for wdt structure\n"); + if (!wdt) return -ENOMEM; - } wdt->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); if (!wdt->regs) { diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c index 9fa1f69dac13..399c3fddecf6 100644 --- a/drivers/watchdog/ath79_wdt.c +++ b/drivers/watchdog/ath79_wdt.c @@ -22,7 +22,6 @@ #include <linux/bitops.h> #include <linux/errno.h> #include <linux/fs.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/miscdevice.h> diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index cafa973c43be..8df450c090a9 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -114,10 +114,8 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) int err; wdt = devm_kzalloc(dev, sizeof(struct bcm2835_wdt), GFP_KERNEL); - if (!wdt) { - dev_err(dev, "Failed to allocate memory for watchdog device"); + if (!wdt) return -ENOMEM; - } platform_set_drvdata(pdev, wdt); spin_lock_init(&wdt->lock); diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index b4021a2b459b..b61fcc535979 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -16,7 +16,6 @@ #include <linux/bcm47xx_wdt.h> #include <linux/bitops.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c index 4eb188b87f8e..5a8e879a430a 100644 --- a/drivers/watchdog/bcm63xx_wdt.c +++ b/drivers/watchdog/bcm63xx_wdt.c @@ -15,7 +15,6 @@ #include <linux/bitops.h> #include <linux/errno.h> #include <linux/fs.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/miscdevice.h> @@ -45,7 +44,6 @@ static struct { void __iomem *regs; struct timer_list timer; - int default_ticks; unsigned long inuse; atomic_t ticks; } bcm63xx_wdt_device; diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c index f7ae49edb518..6d03e8e30f8b 100644 --- a/drivers/watchdog/cpu5wdt.c +++ b/drivers/watchdog/cpu5wdt.c @@ -27,7 +27,6 @@ #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/fs.h> -#include <linux/init.h> #include <linux/ioport.h> #include <linux/timer.h> #include <linux/completion.h> diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index 213225edd059..e55ed702209f 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c @@ -21,7 +21,6 @@ #include <linux/fs.h> #include <linux/errno.h> #include <linux/major.h> -#include <linux/init.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/ioport.h> diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c index f09c54e9686f..2e9589652e1e 100644 --- a/drivers/watchdog/da9052_wdt.c +++ b/drivers/watchdog/da9052_wdt.c @@ -185,7 +185,6 @@ static int da9052_wdt_probe(struct platform_device *pdev) driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), GFP_KERNEL); if (!driver_data) { - dev_err(da9052->dev, "Unable to alloacate watchdog device\n"); ret = -ENOMEM; goto err; } diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c index 575f37a965a4..495089d8dbfe 100644 --- a/drivers/watchdog/da9055_wdt.c +++ b/drivers/watchdog/da9055_wdt.c @@ -151,10 +151,8 @@ static int da9055_wdt_probe(struct platform_device *pdev) driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), GFP_KERNEL); - if (!driver_data) { - dev_err(da9055->dev, "Failed to allocate watchdog device\n"); + if (!driver_data) return -ENOMEM; - } driver_data->da9055 = da9055; diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index b1bae03742a9..d09ad2254b57 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -16,7 +16,6 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/watchdog.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/device.h> diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c index d1d07f2f69df..5f54e1e5819a 100644 --- a/drivers/watchdog/ep93xx_wdt.c +++ b/drivers/watchdog/ep93xx_wdt.c @@ -118,16 +118,9 @@ static int ep93xx_wdt_probe(struct platform_device *pdev) int err; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - - if (!devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name)) - return -EBUSY; - - mmio_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!mmio_base) - return -ENXIO; + mmio_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mmio_base)) + return PTR_ERR(mmio_base); if (timeout < 1 || timeout > 3600) { timeout = WDT_TIMEOUT; @@ -172,9 +165,9 @@ static struct platform_driver ep93xx_wdt_driver = { module_platform_driver(ep93xx_wdt_driver); -MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>," - "Alessandro Zummo <a.zummo@towertech.it>," - "H Hartley Sweeten <hsweeten@visionengravers.com>"); +MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>"); +MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); +MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); MODULE_DESCRIPTION("EP93xx Watchdog"); MODULE_LICENSE("GPL"); MODULE_VERSION(WDT_VERSION); diff --git a/drivers/watchdog/geodewdt.c b/drivers/watchdog/geodewdt.c index 4a6ae84b42bc..4c43e3fa8bd2 100644 --- a/drivers/watchdog/geodewdt.c +++ b/drivers/watchdog/geodewdt.c @@ -215,7 +215,7 @@ static struct miscdevice geodewdt_miscdev = { .fops = &geodewdt_fops, }; -static int geodewdt_probe(struct platform_device *dev) +static int __init geodewdt_probe(struct platform_device *dev) { int ret; @@ -255,7 +255,6 @@ static void geodewdt_shutdown(struct platform_device *dev) } static struct platform_driver geodewdt_driver = { - .probe = geodewdt_probe, .remove = geodewdt_remove, .shutdown = geodewdt_shutdown, .driver = { @@ -268,20 +267,18 @@ static int __init geodewdt_init(void) { int ret; - ret = platform_driver_register(&geodewdt_driver); - if (ret) - return ret; - geodewdt_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); - if (IS_ERR(geodewdt_platform_device)) { - ret = PTR_ERR(geodewdt_platform_device); + if (IS_ERR(geodewdt_platform_device)) + return PTR_ERR(geodewdt_platform_device); + + ret = platform_driver_probe(&geodewdt_driver, geodewdt_probe); + if (ret) goto err; - } return 0; err: - platform_driver_unregister(&geodewdt_driver); + platform_device_unregister(geodewdt_platform_device); return ret; } diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 2b75e8b47279..75d2243b94f5 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -17,7 +17,6 @@ #include <linux/device.h> #include <linux/fs.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/bitops.h> #include <linux/kernel.h> diff --git a/drivers/watchdog/i6300esb.c b/drivers/watchdog/i6300esb.c index 25a2bfdb4e9d..d7befd58b391 100644 --- a/drivers/watchdog/i6300esb.c +++ b/drivers/watchdog/i6300esb.c @@ -36,7 +36,6 @@ #include <linux/mm.h> #include <linux/miscdevice.h> #include <linux/watchdog.h> -#include <linux/init.h> #include <linux/pci.h> #include <linux/ioport.h> #include <linux/uaccess.h> diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 04f8af65acfd..0e6c0333f775 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -347,15 +347,15 @@ static const struct watchdog_info ident = { static const struct watchdog_ops iTCO_wdt_ops = { .owner = THIS_MODULE, .start = iTCO_wdt_start, - .stop = iTCO_wdt_stop, - .ping = iTCO_wdt_ping, + .stop = iTCO_wdt_stop, + .ping = iTCO_wdt_ping, .set_timeout = iTCO_wdt_set_timeout, .get_timeleft = iTCO_wdt_get_timeleft, }; static struct watchdog_device iTCO_wdt_watchdog_dev = { .info = &ident, - .ops = &iTCO_wdt_ops, + .ops = &iTCO_wdt_ops, }; /* @@ -485,7 +485,7 @@ static int iTCO_wdt_probe(struct platform_device *dev) iTCO_wdt_watchdog_dev.bootstatus = 0; iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT; watchdog_set_nowayout(&iTCO_wdt_watchdog_dev, nowayout); - iTCO_wdt_watchdog_dev.parent = dev->dev.parent; + iTCO_wdt_watchdog_dev.parent = &dev->dev; /* Make sure the watchdog is not running */ iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c index 7ae36690c449..4247c498ee78 100644 --- a/drivers/watchdog/ib700wdt.c +++ b/drivers/watchdog/ib700wdt.c @@ -277,7 +277,7 @@ static struct miscdevice ibwdt_miscdev = { * Init & exit routines */ -static int ibwdt_probe(struct platform_device *dev) +static int __init ibwdt_probe(struct platform_device *dev) { int res; @@ -336,7 +336,6 @@ static void ibwdt_shutdown(struct platform_device *dev) } static struct platform_driver ibwdt_driver = { - .probe = ibwdt_probe, .remove = ibwdt_remove, .shutdown = ibwdt_shutdown, .driver = { @@ -351,21 +350,19 @@ static int __init ibwdt_init(void) pr_info("WDT driver for IB700 single board computer initialising\n"); - err = platform_driver_register(&ibwdt_driver); - if (err) - return err; - ibwdt_platform_device = platform_device_register_simple(DRV_NAME, -1, NULL, 0); - if (IS_ERR(ibwdt_platform_device)) { - err = PTR_ERR(ibwdt_platform_device); - goto unreg_platform_driver; - } + if (IS_ERR(ibwdt_platform_device)) + return PTR_ERR(ibwdt_platform_device); + + err = platform_driver_probe(&ibwdt_driver, ibwdt_probe); + if (err) + goto unreg_platform_device; return 0; -unreg_platform_driver: - platform_driver_unregister(&ibwdt_driver); +unreg_platform_device: + platform_device_unregister(ibwdt_platform_device); return err; } diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c index db0a34460e57..366b0474f278 100644 --- a/drivers/watchdog/ibmasr.c +++ b/drivers/watchdog/ibmasr.c @@ -360,7 +360,7 @@ struct ibmasr_id { int type; }; -static struct ibmasr_id __initdata ibmasr_id_table[] = { +static struct ibmasr_id ibmasr_id_table[] __initdata = { { "IBM Automatic Server Restart - eserver xSeries 220", ASMTYPE_TOPAZ }, { "IBM Automatic Server Restart - Machine Type 8673", ASMTYPE_PEARL }, { "IBM Automatic Server Restart - Machine Type 8480", ASMTYPE_JASPER }, diff --git a/drivers/watchdog/indydog.c b/drivers/watchdog/indydog.c index 1b5c25a47b87..5d20cdd30efe 100644 --- a/drivers/watchdog/indydog.c +++ b/drivers/watchdog/indydog.c @@ -41,24 +41,15 @@ MODULE_PARM_DESC(nowayout, static void indydog_start(void) { - u32 mc_ctrl0; - spin_lock(&indydog_lock); - mc_ctrl0 = sgimc->cpuctrl0; - mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG; - sgimc->cpuctrl0 = mc_ctrl0; + sgimc->cpuctrl0 |= SGIMC_CCTRL0_WDOG; spin_unlock(&indydog_lock); } static void indydog_stop(void) { - u32 mc_ctrl0; - spin_lock(&indydog_lock); - - mc_ctrl0 = sgimc->cpuctrl0; - mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG; - sgimc->cpuctrl0 = mc_ctrl0; + sgimc->cpuctrl0 &= ~SGIMC_CCTRL0_WDOG; spin_unlock(&indydog_lock); pr_info("Stopped watchdog timer\n"); diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c index e13e65e996aa..0caab6241eb7 100644 --- a/drivers/watchdog/intel_scu_watchdog.c +++ b/drivers/watchdog/intel_scu_watchdog.c @@ -211,7 +211,6 @@ static int intel_scu_set_heartbeat(u32 t) int ipc_ret; int retry_count; u32 soft_value; - u32 hw_pre_value; u32 hw_value; watchdog_device.timer_set = t; @@ -273,8 +272,7 @@ static int intel_scu_set_heartbeat(u32 t) watchdog_device.timer_load_count_addr); /* read count value before starting timer */ - hw_pre_value = ioread32(watchdog_device.timer_load_count_addr); - hw_pre_value = hw_pre_value & 0xFFFF0000; + ioread32(watchdog_device.timer_load_count_addr); /* Start the timer */ iowrite32(0x00000003, watchdog_device.timer_control_addr); diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index e2bba68ae71e..0b93739c0106 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -54,6 +54,7 @@ /* Defaults for Module Parameter */ #define DEFAULT_NOGAMEPORT 0 +#define DEFAULT_NOCIR 0 #define DEFAULT_EXCLUSIVE 1 #define DEFAULT_TIMEOUT 60 #define DEFAULT_TESTMODE 0 @@ -136,11 +137,13 @@ #define WDTS_LOCKED 3 #define WDTS_USE_GP 4 #define WDTS_EXPECTED 5 +#define WDTS_USE_CIR 6 static unsigned int base, gpact, ciract, max_units, chip_type; static unsigned long wdt_status; static int nogameport = DEFAULT_NOGAMEPORT; +static int nocir = DEFAULT_NOCIR; static int exclusive = DEFAULT_EXCLUSIVE; static int timeout = DEFAULT_TIMEOUT; static int testmode = DEFAULT_TESTMODE; @@ -149,6 +152,9 @@ static bool nowayout = DEFAULT_NOWAYOUT; module_param(nogameport, int, 0); MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default=" __MODULE_STRING(DEFAULT_NOGAMEPORT)); +module_param(nocir, int, 0); +MODULE_PARM_DESC(nocir, "Forbid the use of Consumer IR interrupts to reset timer, default=" + __MODULE_STRING(DEFAULT_NOCIR)); module_param(exclusive, int, 0); MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default=" __MODULE_STRING(DEFAULT_EXCLUSIVE)); @@ -258,9 +264,17 @@ static void wdt_keepalive(void) { if (test_bit(WDTS_USE_GP, &wdt_status)) inb(base); - else + else if (test_bit(WDTS_USE_CIR, &wdt_status)) /* The timer reloads with around 5 msec delay */ outb(0x55, CIR_DR(base)); + else { + if (superio_enter()) + return; + + superio_select(GPIO); + wdt_update_timeout(); + superio_exit(); + } set_bit(WDTS_KEEPALIVE, &wdt_status); } @@ -273,7 +287,7 @@ static int wdt_start(void) superio_select(GPIO); if (test_bit(WDTS_USE_GP, &wdt_status)) superio_outb(WDT_GAMEPORT, WDTCTRL); - else + else if (test_bit(WDTS_USE_CIR, &wdt_status)) superio_outb(WDT_CIRINT, WDTCTRL); wdt_update_timeout(); @@ -660,7 +674,7 @@ static int __init it87_wdt_init(void) } /* If we haven't Gameport support, try to get CIR support */ - if (!test_bit(WDTS_USE_GP, &wdt_status)) { + if (!nocir && !test_bit(WDTS_USE_GP, &wdt_status)) { if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { if (gp_rreq_fail) pr_err("I/O Address 0x%04x and 0x%04x already in use\n", @@ -682,6 +696,7 @@ static int __init it87_wdt_init(void) superio_select(GAMEPORT); superio_outb(gpact, ACTREG); } + set_bit(WDTS_USE_CIR, &wdt_status); } if (timeout < 1 || timeout > max_units * 60) { @@ -707,7 +722,7 @@ static int __init it87_wdt_init(void) } /* Initialize CIR to use it as keepalive source */ - if (!test_bit(WDTS_USE_GP, &wdt_status)) { + if (test_bit(WDTS_USE_CIR, &wdt_status)) { outb(0x00, CIR_RCR(base)); outb(0xc0, CIR_TCR1(base)); outb(0x5c, CIR_TCR2(base)); @@ -717,9 +732,9 @@ static int __init it87_wdt_init(void) outb(0x09, CIR_IER(base)); } - pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d)\n", + pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d nocir=%d)\n", chip_type, chip_rev, timeout, - nowayout, testmode, exclusive, nogameport); + nowayout, testmode, exclusive, nogameport, nocir); superio_exit(); return 0; @@ -727,8 +742,10 @@ static int __init it87_wdt_init(void) err_out_reboot: unregister_reboot_notifier(&wdt_notifier); err_out_region: - release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); - if (!test_bit(WDTS_USE_GP, &wdt_status)) { + if (test_bit(WDTS_USE_GP, &wdt_status)) + release_region(base, 1); + else if (test_bit(WDTS_USE_CIR, &wdt_status)) { + release_region(base, 8); superio_select(CIR); superio_outb(ciract, ACTREG); } @@ -754,7 +771,7 @@ static void __exit it87_wdt_exit(void) if (test_bit(WDTS_USE_GP, &wdt_status)) { superio_select(GAMEPORT); superio_outb(gpact, ACTREG); - } else { + } else if (test_bit(WDTS_USE_CIR, &wdt_status)) { superio_select(CIR); superio_outb(ciract, ACTREG); } @@ -763,7 +780,11 @@ static void __exit it87_wdt_exit(void) misc_deregister(&wdt_miscdev); unregister_reboot_notifier(&wdt_notifier); - release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); + + if (test_bit(WDTS_USE_GP, &wdt_status)) + release_region(base, 1); + else if (test_bit(WDTS_USE_CIR, &wdt_status)) + release_region(base, 8); } module_init(it87_wdt_init); diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c index 3aa50cfa335f..91e45ca589e6 100644 --- a/drivers/watchdog/jz4740_wdt.c +++ b/drivers/watchdog/jz4740_wdt.c @@ -18,7 +18,6 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/watchdog.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/device.h> diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c index bdb3f4a5b27c..0e9cc6f5a919 100644 --- a/drivers/watchdog/max63xx_wdt.c +++ b/drivers/watchdog/max63xx_wdt.c @@ -20,7 +20,6 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/watchdog.h> -#include <linux/init.h> #include <linux/bitops.h> #include <linux/platform_device.h> #include <linux/spinlock.h> diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index c1f65b4c0aa4..7831955cd9e1 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -237,6 +237,7 @@ static const struct of_device_id mpc8xxx_wdt_match[] = { .compatible = "fsl,mpc823-wdt", .data = &(struct mpc8xxx_wdt_type) { .prescaler = 0x800, + .hw_enabled = true, }, }, {}, diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c index edb31ffd7927..ff27c4ac96e4 100644 --- a/drivers/watchdog/mtx-1_wdt.c +++ b/drivers/watchdog/mtx-1_wdt.c @@ -40,7 +40,6 @@ #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/fs.h> -#include <linux/init.h> #include <linux/ioport.h> #include <linux/timer.h> #include <linux/completion.h> diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c index a0d893b0930e..7135803ca1a3 100644 --- a/drivers/watchdog/nuc900_wdt.c +++ b/drivers/watchdog/nuc900_wdt.c @@ -12,7 +12,6 @@ #include <linux/bitops.h> #include <linux/errno.h> #include <linux/fs.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/clk.h> #include <linux/kernel.h> diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c index fb57103c8ebc..57ccae8327ff 100644 --- a/drivers/watchdog/of_xilinx_wdt.c +++ b/drivers/watchdog/of_xilinx_wdt.c @@ -1,6 +1,7 @@ /* * Watchdog Device Driver for Xilinx axi/xps_timebase_wdt * + * (C) Copyright 2013 - 2014 Xilinx, Inc. * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>) * * This program is free software; you can redistribute it and/or @@ -9,18 +10,13 @@ * 2 of the License, or (at your option) any later version. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - +#include <linux/err.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/miscdevice.h> -#include <linux/init.h> #include <linux/ioport.h> #include <linux/watchdog.h> #include <linux/io.h> -#include <linux/uaccess.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_address.h> @@ -43,102 +39,103 @@ #define XWT_TIMER_FAILED 0xFFFFFFFF #define WATCHDOG_NAME "Xilinx Watchdog" -#define PFX WATCHDOG_NAME ": " struct xwdt_device { - struct resource res; void __iomem *base; - u32 nowayout; u32 wdt_interval; - u32 boot_status; + spinlock_t spinlock; + struct watchdog_device xilinx_wdt_wdd; }; -static struct xwdt_device xdev; - -static u32 timeout; -static u32 control_status_reg; -static u8 expect_close; -static u8 no_timeout; -static unsigned long driver_open; - -static DEFINE_SPINLOCK(spinlock); - -static void xwdt_start(void) +static int xilinx_wdt_start(struct watchdog_device *wdd) { - spin_lock(&spinlock); + u32 control_status_reg; + struct xwdt_device *xdev = watchdog_get_drvdata(wdd); + + spin_lock(&xdev->spinlock); /* Clean previous status and enable the watchdog timer */ - control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); + control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET); control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK), - xdev.base + XWT_TWCSR0_OFFSET); + xdev->base + XWT_TWCSR0_OFFSET); + + iowrite32(XWT_CSRX_EWDT2_MASK, xdev->base + XWT_TWCSR1_OFFSET); - iowrite32(XWT_CSRX_EWDT2_MASK, xdev.base + XWT_TWCSR1_OFFSET); + spin_unlock(&xdev->spinlock); - spin_unlock(&spinlock); + return 0; } -static void xwdt_stop(void) +static int xilinx_wdt_stop(struct watchdog_device *wdd) { - spin_lock(&spinlock); + u32 control_status_reg; + struct xwdt_device *xdev = watchdog_get_drvdata(wdd); + + spin_lock(&xdev->spinlock); - control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); + control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET); iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK), - xdev.base + XWT_TWCSR0_OFFSET); + xdev->base + XWT_TWCSR0_OFFSET); - iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET); + iowrite32(0, xdev->base + XWT_TWCSR1_OFFSET); - spin_unlock(&spinlock); + spin_unlock(&xdev->spinlock); pr_info("Stopped!\n"); + + return 0; } -static void xwdt_keepalive(void) +static int xilinx_wdt_keepalive(struct watchdog_device *wdd) { - spin_lock(&spinlock); + u32 control_status_reg; + struct xwdt_device *xdev = watchdog_get_drvdata(wdd); - control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); - control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); - iowrite32(control_status_reg, xdev.base + XWT_TWCSR0_OFFSET); + spin_lock(&xdev->spinlock); - spin_unlock(&spinlock); -} + control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET); + control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); + iowrite32(control_status_reg, xdev->base + XWT_TWCSR0_OFFSET); -static void xwdt_get_status(int *status) -{ - int new_status; + spin_unlock(&xdev->spinlock); - spin_lock(&spinlock); + return 0; +} - control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); - new_status = ((control_status_reg & - (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK)) != 0); - spin_unlock(&spinlock); +static const struct watchdog_info xilinx_wdt_ident = { + .options = WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .firmware_version = 1, + .identity = WATCHDOG_NAME, +}; - *status = 0; - if (new_status & 1) - *status |= WDIOF_CARDRESET; -} +static const struct watchdog_ops xilinx_wdt_ops = { + .owner = THIS_MODULE, + .start = xilinx_wdt_start, + .stop = xilinx_wdt_stop, + .ping = xilinx_wdt_keepalive, +}; -static u32 xwdt_selftest(void) +static u32 xwdt_selftest(struct xwdt_device *xdev) { int i; u32 timer_value1; u32 timer_value2; - spin_lock(&spinlock); + spin_lock(&xdev->spinlock); - timer_value1 = ioread32(xdev.base + XWT_TBR_OFFSET); - timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); + timer_value1 = ioread32(xdev->base + XWT_TBR_OFFSET); + timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET); for (i = 0; ((i <= XWT_MAX_SELFTEST_LOOP_COUNT) && (timer_value2 == timer_value1)); i++) { - timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); + timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET); } - spin_unlock(&spinlock); + spin_unlock(&xdev->spinlock); if (timer_value2 != timer_value1) return ~XWT_TIMER_FAILED; @@ -146,238 +143,83 @@ static u32 xwdt_selftest(void) return XWT_TIMER_FAILED; } -static int xwdt_open(struct inode *inode, struct file *file) -{ - /* Only one process can handle the wdt at a time */ - if (test_and_set_bit(0, &driver_open)) - return -EBUSY; - - /* Make sure that the module are always loaded...*/ - if (xdev.nowayout) - __module_get(THIS_MODULE); - - xwdt_start(); - pr_info("Started...\n"); - - return nonseekable_open(inode, file); -} - -static int xwdt_release(struct inode *inode, struct file *file) -{ - if (expect_close == 42) { - xwdt_stop(); - } else { - pr_crit("Unexpected close, not stopping watchdog!\n"); - xwdt_keepalive(); - } - - clear_bit(0, &driver_open); - expect_close = 0; - return 0; -} - -/* - * xwdt_write: - * @file: file handle to the watchdog - * @buf: buffer to write (unused as data does not matter here - * @count: count of bytes - * @ppos: pointer to the position to write. No seeks allowed - * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we don't define content meaning. - */ -static ssize_t xwdt_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) -{ - if (len) { - if (!xdev.nowayout) { - size_t i; - - /* In case it was set long ago */ - expect_close = 0; - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = 42; - } - } - xwdt_keepalive(); - } - return len; -} - -static const struct watchdog_info ident = { - .options = WDIOF_MAGICCLOSE | - WDIOF_KEEPALIVEPING, - .firmware_version = 1, - .identity = WATCHDOG_NAME, -}; - -/* - * xwdt_ioctl: - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer - * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. - */ -static long xwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int status; - - union { - struct watchdog_info __user *ident; - int __user *i; - } uarg; - - uarg.i = (int __user *)arg; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(uarg.ident, &ident, - sizeof(ident)) ? -EFAULT : 0; - - case WDIOC_GETBOOTSTATUS: - return put_user(xdev.boot_status, uarg.i); - - case WDIOC_GETSTATUS: - xwdt_get_status(&status); - return put_user(status, uarg.i); - - case WDIOC_KEEPALIVE: - xwdt_keepalive(); - return 0; - - case WDIOC_GETTIMEOUT: - if (no_timeout) - return -ENOTTY; - else - return put_user(timeout, uarg.i); - - default: - return -ENOTTY; - } -} - -static const struct file_operations xwdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .write = xwdt_write, - .open = xwdt_open, - .release = xwdt_release, - .unlocked_ioctl = xwdt_ioctl, -}; - -static struct miscdevice xwdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &xwdt_fops, -}; - static int xwdt_probe(struct platform_device *pdev) { int rc; - u32 *tmptr; - u32 *pfreq; - - no_timeout = 0; - - pfreq = (u32 *)of_get_property(pdev->dev.of_node, - "clock-frequency", NULL); - - if (pfreq == NULL) { - pr_warn("The watchdog clock frequency cannot be obtained!\n"); - no_timeout = 1; - } - - rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res); - if (rc) { - pr_warn("invalid address!\n"); - return rc; - } - - tmptr = (u32 *)of_get_property(pdev->dev.of_node, - "xlnx,wdt-interval", NULL); - if (tmptr == NULL) { - pr_warn("Parameter \"xlnx,wdt-interval\" not found in device tree!\n"); - no_timeout = 1; - } else { - xdev.wdt_interval = *tmptr; - } - - tmptr = (u32 *)of_get_property(pdev->dev.of_node, - "xlnx,wdt-enable-once", NULL); - if (tmptr == NULL) { - pr_warn("Parameter \"xlnx,wdt-enable-once\" not found in device tree!\n"); - xdev.nowayout = WATCHDOG_NOWAYOUT; - } - -/* - * Twice of the 2^wdt_interval / freq because the first wdt overflow is - * ignored (interrupt), reset is only generated at second wdt overflow - */ - if (!no_timeout) - timeout = 2 * ((1<<xdev.wdt_interval) / *pfreq); - - if (!request_mem_region(xdev.res.start, - xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) { - rc = -ENXIO; - pr_err("memory request failure!\n"); - goto err_out; - } - - xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1); - if (xdev.base == NULL) { - rc = -ENOMEM; - pr_err("ioremap failure!\n"); - goto release_mem; - } - - rc = xwdt_selftest(); + u32 pfreq = 0, enable_once = 0; + struct resource *res; + struct xwdt_device *xdev; + struct watchdog_device *xilinx_wdt_wdd; + + xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL); + if (!xdev) + return -ENOMEM; + + xilinx_wdt_wdd = &xdev->xilinx_wdt_wdd; + xilinx_wdt_wdd->info = &xilinx_wdt_ident; + xilinx_wdt_wdd->ops = &xilinx_wdt_ops; + xilinx_wdt_wdd->parent = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xdev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xdev->base)) + return PTR_ERR(xdev->base); + + rc = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &pfreq); + if (rc) + dev_warn(&pdev->dev, + "The watchdog clock frequency cannot be obtained\n"); + + rc = of_property_read_u32(pdev->dev.of_node, "xlnx,wdt-interval", + &xdev->wdt_interval); + if (rc) + dev_warn(&pdev->dev, + "Parameter \"xlnx,wdt-interval\" not found\n"); + + rc = of_property_read_u32(pdev->dev.of_node, "xlnx,wdt-enable-once", + &enable_once); + if (rc) + dev_warn(&pdev->dev, + "Parameter \"xlnx,wdt-enable-once\" not found\n"); + + watchdog_set_nowayout(xilinx_wdt_wdd, enable_once); + + /* + * Twice of the 2^wdt_interval / freq because the first wdt overflow is + * ignored (interrupt), reset is only generated at second wdt overflow + */ + if (pfreq && xdev->wdt_interval) + xilinx_wdt_wdd->timeout = 2 * ((1 << xdev->wdt_interval) / + pfreq); + + spin_lock_init(&xdev->spinlock); + watchdog_set_drvdata(xilinx_wdt_wdd, xdev); + + rc = xwdt_selftest(xdev); if (rc == XWT_TIMER_FAILED) { - pr_err("SelfTest routine error!\n"); - goto unmap_io; + dev_err(&pdev->dev, "SelfTest routine error\n"); + return rc; } - xwdt_get_status(&xdev.boot_status); - - rc = misc_register(&xwdt_miscdev); + rc = watchdog_register_device(xilinx_wdt_wdd); if (rc) { - pr_err("cannot register miscdev on minor=%d (err=%d)\n", - xwdt_miscdev.minor, rc); - goto unmap_io; + dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc); + return rc; } - if (no_timeout) - pr_info("driver loaded (timeout=? sec, nowayout=%d)\n", - xdev.nowayout); - else - pr_info("driver loaded (timeout=%d sec, nowayout=%d)\n", - timeout, xdev.nowayout); + dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n", + xdev->base, xilinx_wdt_wdd->timeout); - expect_close = 0; - clear_bit(0, &driver_open); + platform_set_drvdata(pdev, xdev); return 0; - -unmap_io: - iounmap(xdev.base); -release_mem: - release_mem_region(xdev.res.start, resource_size(&xdev.res)); -err_out: - return rc; } -static int xwdt_remove(struct platform_device *dev) +static int xwdt_remove(struct platform_device *pdev) { - misc_deregister(&xwdt_miscdev); - iounmap(xdev.base); - release_mem_region(xdev.res.start, resource_size(&xdev.res)); + struct xwdt_device *xdev = platform_get_drvdata(pdev); + + watchdog_unregister_device(&xdev->xilinx_wdt_wdd); return 0; } diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 09cf0135e8ac..3691b157516a 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -34,7 +34,6 @@ #include <linux/mm.h> #include <linux/watchdog.h> #include <linux/reboot.h> -#include <linux/init.h> #include <linux/err.h> #include <linux/platform_device.h> #include <linux/moduleparam.h> @@ -58,7 +57,6 @@ struct omap_wdt_dev { void __iomem *base; /* physical */ struct device *dev; bool omap_wdt_users; - struct resource *mem; int wdt_trgr_pattern; struct mutex lock; /* to avoid races with PM */ }; @@ -207,7 +205,7 @@ static int omap_wdt_probe(struct platform_device *pdev) { struct omap_wd_timer_platform_data *pdata = dev_get_platdata(&pdev->dev); struct watchdog_device *omap_wdt; - struct resource *res, *mem; + struct resource *res; struct omap_wdt_dev *wdev; u32 rs; int ret; @@ -216,29 +214,20 @@ static int omap_wdt_probe(struct platform_device *pdev) if (!omap_wdt) return -ENOMEM; - /* reserve static register mappings */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; - - mem = devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), pdev->name); - if (!mem) - return -EBUSY; - wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); if (!wdev) return -ENOMEM; wdev->omap_wdt_users = false; - wdev->mem = mem; wdev->dev = &pdev->dev; wdev->wdt_trgr_pattern = 0x1234; mutex_init(&wdev->lock); - wdev->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!wdev->base) - return -ENOMEM; + /* reserve static register mappings */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wdev->base)) + return PTR_ERR(wdev->base); omap_wdt->info = &omap_wdt_info; omap_wdt->ops = &omap_wdt_ops; diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index f7722a424676..498163497c1c 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -18,7 +18,6 @@ #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/watchdog.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/spinlock.h> #include <linux/clk.h> diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index 5211d56b3681..9f15dd9435d1 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c @@ -512,9 +512,8 @@ static int __init pc87413_init(void) return -EBUSY; ret = register_reboot_notifier(&pc87413_notifier); - if (ret != 0) { + if (ret != 0) pr_err("cannot register reboot notifier (err=%d)\n", ret); - } ret = misc_register(&pc87413_miscdev); if (ret != 0) { @@ -575,8 +574,8 @@ static void __exit pc87413_exit(void) module_init(pc87413_init); module_exit(pc87413_exit); -MODULE_AUTHOR("Sven Anders <anders@anduras.de>, " - "Marcus Junker <junker@anduras.de>,"); +MODULE_AUTHOR("Sven Anders <anders@anduras.de>"); +MODULE_AUTHOR("Marcus Junker <junker@anduras.de>"); MODULE_DESCRIPTION("PC87413 WDT driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index e562e0476016..1a11aedc4fe8 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -645,10 +645,8 @@ static int usb_pcwd_probe(struct usb_interface *interface, /* allocate memory for our device and initialize it */ usb_pcwd = kzalloc(sizeof(struct usb_pcwd_private), GFP_KERNEL); - if (usb_pcwd == NULL) { - pr_err("Out of memory\n"); + if (usb_pcwd == NULL) goto error; - } usb_pcwd_device = usb_pcwd; diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c index 5bec20f5dc2d..15fb316e9437 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c @@ -24,7 +24,6 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/watchdog.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/spinlock.h> diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c index 082d06262959..29cf4dcbc59c 100644 --- a/drivers/watchdog/rdc321x_wdt.c +++ b/drivers/watchdog/rdc321x_wdt.c @@ -27,7 +27,6 @@ #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/fs.h> -#include <linux/init.h> #include <linux/ioport.h> #include <linux/timer.h> #include <linux/completion.h> diff --git a/drivers/watchdog/retu_wdt.c b/drivers/watchdog/retu_wdt.c index f53615dc633d..a7a0695971e4 100644 --- a/drivers/watchdog/retu_wdt.c +++ b/drivers/watchdog/retu_wdt.c @@ -16,7 +16,6 @@ * GNU General Public License for more details. */ -#include <linux/init.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/device.h> diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index 3dd8ed28adc8..cfed0fe264dc 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -10,7 +10,6 @@ #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/of.h> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index aec946df6ed9..7c6ccd071baf 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -30,7 +30,6 @@ #include <linux/types.h> #include <linux/timer.h> #include <linux/watchdog.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/clk.h> @@ -526,7 +525,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev) goto err; } - clk_prepare_enable(wdt->clock); + ret = clk_prepare_enable(wdt->clock); + if (ret < 0) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } ret = s3c2410wdt_cpufreq_register(wdt); if (ret < 0) { @@ -608,7 +611,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev) err_clk: clk_disable_unprepare(wdt->clock); - wdt->clock = NULL; err: return ret; @@ -628,7 +630,6 @@ static int s3c2410wdt_remove(struct platform_device *dev) s3c2410wdt_cpufreq_deregister(wdt); clk_disable_unprepare(wdt->clock); - wdt->clock = NULL; return 0; } diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c index f353e18b1a82..1cfd3f6a13d5 100644 --- a/drivers/watchdog/sc520_wdt.c +++ b/drivers/watchdog/sc520_wdt.c @@ -158,12 +158,11 @@ static void wdt_timer_ping(unsigned long data) static void wdt_config(int writeval) { - __u16 dummy; unsigned long flags; /* buy some time (ping) */ spin_lock_irqsave(&wdt_spinlock, flags); - dummy = readw(wdtmrctl); /* ensure write synchronization */ + readw(wdtmrctl); /* ensure write synchronization */ writew(0xAAAA, wdtmrctl); writew(0x5555, wdtmrctl); /* unlock WDT = make WDT configuration register writable one time */ diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index af3528f84d65..d04d02b41c32 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c @@ -293,8 +293,6 @@ static int sh_wdt_probe(struct platform_device *pdev) static int sh_wdt_remove(struct platform_device *pdev) { - struct sh_wdt *wdt = platform_get_drvdata(pdev); - watchdog_unregister_device(&sh_wdt_dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index c04a1aa158e2..0dc5e323d59d 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -62,7 +62,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -static int soft_noboot = 0; +static int soft_noboot; module_param(soft_noboot, int, 0); MODULE_PARM_DESC(soft_noboot, "Softdog action, set to 1 to ignore reboots, 0 to reboot (default=0)"); diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 3f786ce0a6f2..47629d268e0a 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -16,7 +16,6 @@ #include <linux/amba/bus.h> #include <linux/bitops.h> #include <linux/clk.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/kernel.h> @@ -209,27 +208,15 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) struct sp805_wdt *wdt; int ret = 0; - if (!devm_request_mem_region(&adev->dev, adev->res.start, - resource_size(&adev->res), "sp805_wdt")) { - dev_warn(&adev->dev, "Failed to get memory region resource\n"); - ret = -ENOENT; - goto err; - } - wdt = devm_kzalloc(&adev->dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) { - dev_warn(&adev->dev, "Kzalloc failed\n"); ret = -ENOMEM; goto err; } - wdt->base = devm_ioremap(&adev->dev, adev->res.start, - resource_size(&adev->res)); - if (!wdt->base) { - ret = -ENOMEM; - dev_warn(&adev->dev, "ioremap fail\n"); - goto err; - } + wdt->base = devm_ioremap_resource(&adev->dev, &adev->res); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); wdt->clk = devm_clk_get(&adev->dev, NULL); if (IS_ERR(wdt->clk)) { diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c index bb64ae3f47da..3804d5e9baea 100644 --- a/drivers/watchdog/stmp3xxx_rtc_wdt.c +++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c @@ -9,7 +9,6 @@ * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/watchdog.h> diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 76332d893e12..cd00a7836cdc 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -205,7 +205,7 @@ static void sunxi_wdt_shutdown(struct platform_device *pdev) } static const struct of_device_id sunxi_wdt_dt_ids[] = { - { .compatible = "allwinner,sun4i-wdt" }, + { .compatible = "allwinner,sun4i-a10-wdt" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c new file mode 100644 index 000000000000..750e2a26cb12 --- /dev/null +++ b/drivers/watchdog/tegra_wdt.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +/* minimum and maximum watchdog trigger timeout, in seconds */ +#define MIN_WDT_TIMEOUT 1 +#define MAX_WDT_TIMEOUT 255 + +/* + * Base of the WDT registers, from the timer base address. There are + * actually 5 watchdogs that can be configured (by pairing with an available + * timer), at bases 0x100 + (WDT ID) * 0x20, where WDT ID is 0 through 4. + * This driver only configures the first watchdog (WDT ID 0). + */ +#define WDT_BASE 0x100 +#define WDT_ID 0 + +/* + * Register base of the timer that's selected for pairing with the watchdog. + * This driver arbitrarily uses timer 5, which is currently unused by + * other drivers (in particular, the Tegra clocksource driver). If this + * needs to change, take care that the new timer is not used by the + * clocksource driver. + */ +#define WDT_TIMER_BASE 0x60 +#define WDT_TIMER_ID 5 + +/* WDT registers */ +#define WDT_CFG 0x0 +#define WDT_CFG_PERIOD_SHIFT 4 +#define WDT_CFG_PERIOD_MASK 0xff +#define WDT_CFG_INT_EN (1 << 12) +#define WDT_CFG_PMC2CAR_RST_EN (1 << 15) +#define WDT_STS 0x4 +#define WDT_STS_COUNT_SHIFT 4 +#define WDT_STS_COUNT_MASK 0xff +#define WDT_STS_EXP_SHIFT 12 +#define WDT_STS_EXP_MASK 0x3 +#define WDT_CMD 0x8 +#define WDT_CMD_START_COUNTER (1 << 0) +#define WDT_CMD_DISABLE_COUNTER (1 << 1) +#define WDT_UNLOCK (0xc) +#define WDT_UNLOCK_PATTERN (0xc45a << 0) + +/* Timer registers */ +#define TIMER_PTV 0x0 +#define TIMER_EN (1 << 31) +#define TIMER_PERIODIC (1 << 30) + +struct tegra_wdt { + struct watchdog_device wdd; + void __iomem *wdt_regs; + void __iomem *tmr_regs; +}; + +#define WDT_HEARTBEAT 120 +static int heartbeat = WDT_HEARTBEAT; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, + "Watchdog heartbeats in seconds. (default = " + __MODULE_STRING(WDT_HEARTBEAT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int tegra_wdt_start(struct watchdog_device *wdd) +{ + struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); + u32 val; + + /* + * This thing has a fixed 1MHz clock. Normally, we would set the + * period to 1 second by writing 1000000ul, but the watchdog system + * reset actually occurs on the 4th expiration of this counter, + * so we set the period to 1/4 of this amount. + */ + val = 1000000ul / 4; + val |= (TIMER_EN | TIMER_PERIODIC); + writel(val, wdt->tmr_regs + TIMER_PTV); + + /* + * Set number of periods and start counter. + * + * Interrupt handler is not required for user space + * WDT accesses, since the caller is responsible to ping the + * WDT to reset the counter before expiration, through ioctls. + */ + val = WDT_TIMER_ID | + (wdd->timeout << WDT_CFG_PERIOD_SHIFT) | + WDT_CFG_PMC2CAR_RST_EN; + writel(val, wdt->wdt_regs + WDT_CFG); + + writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD); + + return 0; +} + +static int tegra_wdt_stop(struct watchdog_device *wdd) +{ + struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); + + writel(WDT_UNLOCK_PATTERN, wdt->wdt_regs + WDT_UNLOCK); + writel(WDT_CMD_DISABLE_COUNTER, wdt->wdt_regs + WDT_CMD); + writel(0, wdt->tmr_regs + TIMER_PTV); + + return 0; +} + +static int tegra_wdt_ping(struct watchdog_device *wdd) +{ + struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); + + writel(WDT_CMD_START_COUNTER, wdt->wdt_regs + WDT_CMD); + + return 0; +} + +static int tegra_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + wdd->timeout = timeout; + + if (watchdog_active(wdd)) + return tegra_wdt_start(wdd); + + return 0; +} + +static unsigned int tegra_wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct tegra_wdt *wdt = watchdog_get_drvdata(wdd); + u32 val; + int count; + int exp; + + val = readl(wdt->wdt_regs + WDT_STS); + + /* Current countdown (from timeout) */ + count = (val >> WDT_STS_COUNT_SHIFT) & WDT_STS_COUNT_MASK; + + /* Number of expirations (we are waiting for the 4th expiration) */ + exp = (val >> WDT_STS_EXP_SHIFT) & WDT_STS_EXP_MASK; + + /* + * The entire thing is divided by 4 because we are ticking down 4 times + * faster due to needing to wait for the 4th expiration. + */ + return (((3 - exp) * wdd->timeout) + count) / 4; +} + +static const struct watchdog_info tegra_wdt_info = { + .options = WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, + .firmware_version = 0, + .identity = "Tegra Watchdog", +}; + +static struct watchdog_ops tegra_wdt_ops = { + .owner = THIS_MODULE, + .start = tegra_wdt_start, + .stop = tegra_wdt_stop, + .ping = tegra_wdt_ping, + .set_timeout = tegra_wdt_set_timeout, + .get_timeleft = tegra_wdt_get_timeleft, +}; + +static int tegra_wdt_probe(struct platform_device *pdev) +{ + struct watchdog_device *wdd; + struct tegra_wdt *wdt; + struct resource *res; + void __iomem *regs; + int ret; + + /* This is the timer base. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + /* + * Allocate our watchdog driver data, which has the + * struct watchdog_device nested within it. + */ + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + /* Initialize struct tegra_wdt. */ + wdt->wdt_regs = regs + WDT_BASE; + wdt->tmr_regs = regs + WDT_TIMER_BASE; + + /* Initialize struct watchdog_device. */ + wdd = &wdt->wdd; + wdd->timeout = heartbeat; + wdd->info = &tegra_wdt_info; + wdd->ops = &tegra_wdt_ops; + wdd->min_timeout = MIN_WDT_TIMEOUT; + wdd->max_timeout = MAX_WDT_TIMEOUT; + + watchdog_set_drvdata(wdd, wdt); + + watchdog_set_nowayout(wdd, nowayout); + + ret = watchdog_register_device(wdd); + if (ret) { + dev_err(&pdev->dev, + "failed to register watchdog device\n"); + return ret; + } + + platform_set_drvdata(pdev, wdt); + + dev_info(&pdev->dev, + "initialized (heartbeat = %d sec, nowayout = %d)\n", + heartbeat, nowayout); + + return 0; +} + +static int tegra_wdt_remove(struct platform_device *pdev) +{ + struct tegra_wdt *wdt = platform_get_drvdata(pdev); + + tegra_wdt_stop(&wdt->wdd); + + watchdog_unregister_device(&wdt->wdd); + + dev_info(&pdev->dev, "removed wdt\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_wdt_runtime_suspend(struct device *dev) +{ + struct tegra_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + tegra_wdt_stop(&wdt->wdd); + + return 0; +} + +static int tegra_wdt_runtime_resume(struct device *dev) +{ + struct tegra_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + tegra_wdt_start(&wdt->wdd); + + return 0; +} +#endif + +static const struct of_device_id tegra_wdt_of_match[] = { + { .compatible = "nvidia,tegra30-timer", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_wdt_of_match); + +static const struct dev_pm_ops tegra_wdt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tegra_wdt_runtime_suspend, + tegra_wdt_runtime_resume) +}; + +static struct platform_driver tegra_wdt_driver = { + .probe = tegra_wdt_probe, + .remove = tegra_wdt_remove, + .driver = { + .owner = THIS_MODULE, + .name = "tegra-wdt", + .pm = &tegra_wdt_pm_ops, + .of_match_table = tegra_wdt_of_match, + }, +}; +module_platform_driver(tegra_wdt_driver); + +MODULE_AUTHOR("NVIDIA Corporation"); +MODULE_DESCRIPTION("Tegra Watchdog Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index 09d4831aa61f..afa9d6ef353a 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c @@ -61,7 +61,7 @@ struct ts72xx_wdt { struct platform_device *pdev; }; -struct platform_device *ts72xx_wdt_pdev; +static struct platform_device *ts72xx_wdt_pdev; /* * TS-72xx Watchdog supports following timeouts (value written @@ -394,10 +394,8 @@ static int ts72xx_wdt_probe(struct platform_device *pdev) int error = 0; wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL); - if (!wdt) { - dev_err(&pdev->dev, "failed to allocate memory\n"); + if (!wdt) return -ENOMEM; - } r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1); diff --git a/drivers/watchdog/w83697hf_wdt.c b/drivers/watchdog/w83697hf_wdt.c index 68b45fc9ba6a..e9ea856b8ff2 100644 --- a/drivers/watchdog/w83697hf_wdt.c +++ b/drivers/watchdog/w83697hf_wdt.c @@ -455,6 +455,6 @@ module_init(wdt_init); module_exit(wdt_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Marcus Junker <junker@anduras.de>, " - "Samuel Tardieu <sam@rfc1149.net>"); +MODULE_AUTHOR("Marcus Junker <junker@anduras.de>"); +MODULE_AUTHOR("Samuel Tardieu <sam@rfc1149.net>"); MODULE_DESCRIPTION("w83697hf/hg WDT driver"); diff --git a/drivers/watchdog/wdt285.c b/drivers/watchdog/wdt285.c index 7355ddd0b207..ebbb183be618 100644 --- a/drivers/watchdog/wdt285.c +++ b/drivers/watchdog/wdt285.c @@ -139,9 +139,8 @@ static const struct watchdog_info ident = { static long watchdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - unsigned int new_margin; int __user *int_arg = (int __user *)arg; - int ret = -ENOTTY; + int new_margin, ret = -ENOTTY; switch (cmd) { case WDIOC_GETSUPPORT: diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index 3dc578e71211..48b2c058b009 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c @@ -49,7 +49,6 @@ #include <linux/delay.h> #include <linux/notifier.h> #include <linux/reboot.h> -#include <linux/init.h> #include <linux/fs.h> #include <linux/pci.h> #include <linux/io.h> diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c index e243bd01c774..2fa17e746ff6 100644 --- a/drivers/watchdog/wm831x_wdt.c +++ b/drivers/watchdog/wm831x_wdt.c @@ -204,7 +204,6 @@ static int wm831x_wdt_probe(struct platform_device *pdev) driver_data = devm_kzalloc(&pdev->dev, sizeof(*driver_data), GFP_KERNEL); if (!driver_data) { - dev_err(wm831x->dev, "Unable to alloacate watchdog device\n"); ret = -ENOMEM; goto err; } diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 37d06ea624aa..61a6ac8fa8fc 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -399,11 +399,25 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp) state = BP_EAGAIN; break; } + scrub_page(page); - pfn = page_to_pfn(page); - frame_list[i] = pfn_to_mfn(pfn); + frame_list[i] = page_to_pfn(page); + } - scrub_page(page); + /* + * Ensure that ballooned highmem pages don't have kmaps. + * + * Do this before changing the p2m as kmap_flush_unused() + * reads PTEs to obtain pages (and hence needs the original + * p2m entry). + */ + kmap_flush_unused(); + + /* Update direct mapping, invalidate P2M, and add to balloon. */ + for (i = 0; i < nr_pages; i++) { + pfn = frame_list[i]; + frame_list[i] = pfn_to_mfn(pfn); + page = pfn_to_page(pfn); #ifdef CONFIG_XEN_HAVE_PVMMU /* @@ -429,11 +443,9 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp) } #endif - balloon_append(pfn_to_page(pfn)); + balloon_append(page); } - /* Ensure that ballooned highmem pages don't have kmaps. */ - kmap_flush_unused(); flush_tlb_all(); set_xen_guest_handle(reservation.extent_start, frame_list); diff --git a/drivers/xen/events/events_2l.c b/drivers/xen/events/events_2l.c index d7ff91757307..5db43fc100a4 100644 --- a/drivers/xen/events/events_2l.c +++ b/drivers/xen/events/events_2l.c @@ -166,7 +166,6 @@ static void evtchn_2l_handle_events(unsigned cpu) int start_word_idx, start_bit_idx; int word_idx, bit_idx; int i; - struct irq_desc *desc; struct shared_info *s = HYPERVISOR_shared_info; struct vcpu_info *vcpu_info = __this_cpu_read(xen_vcpu); @@ -176,11 +175,8 @@ static void evtchn_2l_handle_events(unsigned cpu) unsigned int evtchn = evtchn_from_irq(irq); word_idx = evtchn / BITS_PER_LONG; bit_idx = evtchn % BITS_PER_LONG; - if (active_evtchns(cpu, s, word_idx) & (1ULL << bit_idx)) { - desc = irq_to_desc(irq); - if (desc) - generic_handle_irq_desc(irq, desc); - } + if (active_evtchns(cpu, s, word_idx) & (1ULL << bit_idx)) + generic_handle_irq(irq); } /* @@ -245,11 +241,8 @@ static void evtchn_2l_handle_events(unsigned cpu) port = (word_idx * BITS_PER_EVTCHN_WORD) + bit_idx; irq = get_evtchn_to_irq(port); - if (irq != -1) { - desc = irq_to_desc(irq); - if (desc) - generic_handle_irq_desc(irq, desc); - } + if (irq != -1) + generic_handle_irq(irq); bit_idx = (bit_idx + 1) % BITS_PER_EVTCHN_WORD; diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index f4a9e3311297..c3458f58de90 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -336,9 +336,8 @@ static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu) BUG_ON(irq == -1); #ifdef CONFIG_SMP - cpumask_copy(irq_to_desc(irq)->irq_data.affinity, cpumask_of(cpu)); + cpumask_copy(irq_get_irq_data(irq)->affinity, cpumask_of(cpu)); #endif - xen_evtchn_port_bind_to_cpu(info, cpu); info->cpu = cpu; @@ -373,10 +372,8 @@ static void xen_irq_init(unsigned irq) { struct irq_info *info; #ifdef CONFIG_SMP - struct irq_desc *desc = irq_to_desc(irq); - /* By default all event channels notify CPU#0. */ - cpumask_copy(desc->irq_data.affinity, cpumask_of(0)); + cpumask_copy(irq_get_irq_data(irq)->affinity, cpumask_of(0)); #endif info = kzalloc(sizeof(*info), GFP_KERNEL); @@ -490,13 +487,6 @@ static void pirq_query_unmask(int irq) info->u.pirq.flags |= PIRQ_NEEDS_EOI; } -static bool probing_irq(int irq) -{ - struct irq_desc *desc = irq_to_desc(irq); - - return desc && desc->action == NULL; -} - static void eoi_pirq(struct irq_data *data) { int evtchn = evtchn_from_irq(data->irq); @@ -538,8 +528,7 @@ static unsigned int __startup_pirq(unsigned int irq) BIND_PIRQ__WILL_SHARE : 0; rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &bind_pirq); if (rc != 0) { - if (!probing_irq(irq)) - pr_info("Failed to obtain physical IRQ %d\n", irq); + pr_warn("Failed to obtain physical IRQ %d\n", irq); return 0; } evtchn = bind_pirq.port; @@ -772,17 +761,12 @@ error_irq: int xen_destroy_irq(int irq) { - struct irq_desc *desc; struct physdev_unmap_pirq unmap_irq; struct irq_info *info = info_for_irq(irq); int rc = -ENOENT; mutex_lock(&irq_mapping_update_lock); - desc = irq_to_desc(irq); - if (!desc) - goto out; - if (xen_initial_domain()) { unmap_irq.pirq = info->u.pirq.pirq; unmap_irq.domid = info->u.pirq.domid; @@ -1251,6 +1235,7 @@ void xen_evtchn_do_upcall(struct pt_regs *regs) #ifdef CONFIG_X86 exit_idle(); #endif + inc_irq_stat(irq_hv_callback_count); __xen_evtchn_do_upcall(); @@ -1339,7 +1324,7 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) static int set_affinity_irq(struct irq_data *data, const struct cpumask *dest, bool force) { - unsigned tcpu = cpumask_first(dest); + unsigned tcpu = cpumask_first_and(dest, cpu_online_mask); return rebind_irq_to_cpu(data->irq, tcpu); } diff --git a/drivers/xen/events/events_fifo.c b/drivers/xen/events/events_fifo.c index 1de2a191b395..96109a9972b6 100644 --- a/drivers/xen/events/events_fifo.c +++ b/drivers/xen/events/events_fifo.c @@ -235,14 +235,10 @@ static uint32_t clear_linked(volatile event_word_t *word) static void handle_irq_for_port(unsigned port) { int irq; - struct irq_desc *desc; irq = get_evtchn_to_irq(port); - if (irq != -1) { - desc = irq_to_desc(irq); - if (desc) - generic_handle_irq_desc(irq, desc); - } + if (irq != -1) + generic_handle_irq(irq); } static void consume_one_event(unsigned cpu, diff --git a/drivers/xen/xen-acpi-cpuhotplug.c b/drivers/xen/xen-acpi-cpuhotplug.c index 80875fb770ed..3e62ee4b3b66 100644 --- a/drivers/xen/xen-acpi-cpuhotplug.c +++ b/drivers/xen/xen-acpi-cpuhotplug.c @@ -313,7 +313,7 @@ static void acpi_processor_hotplug_notify(acpi_handle handle, goto out; } - (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL); + (void) acpi_evaluate_ost(handle, event, ost_code, NULL); out: acpi_scan_lock_release(); diff --git a/drivers/xen/xen-acpi-memhotplug.c b/drivers/xen/xen-acpi-memhotplug.c index f8d18626969a..34e40b733f9a 100644 --- a/drivers/xen/xen-acpi-memhotplug.c +++ b/drivers/xen/xen-acpi-memhotplug.c @@ -285,7 +285,7 @@ static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) return; } - (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL); + (void) acpi_evaluate_ost(handle, event, ost_code, NULL); return; } diff --git a/drivers/xen/xen-acpi-pad.c b/drivers/xen/xen-acpi-pad.c index 40c4bc06b5fa..f83b754505f8 100644 --- a/drivers/xen/xen-acpi-pad.c +++ b/drivers/xen/xen-acpi-pad.c @@ -77,27 +77,14 @@ static int acpi_pad_pur(acpi_handle handle) return num; } -/* Notify firmware how many CPUs are idle */ -static void acpi_pad_ost(acpi_handle handle, int stat, - uint32_t idle_nums) -{ - union acpi_object params[3] = { - {.type = ACPI_TYPE_INTEGER,}, - {.type = ACPI_TYPE_INTEGER,}, - {.type = ACPI_TYPE_BUFFER,}, - }; - struct acpi_object_list arg_list = {3, params}; - - params[0].integer.value = ACPI_PROCESSOR_AGGREGATOR_NOTIFY; - params[1].integer.value = stat; - params[2].buffer.length = 4; - params[2].buffer.pointer = (void *)&idle_nums; - acpi_evaluate_object(handle, "_OST", &arg_list, NULL); -} - static void acpi_pad_handle_notify(acpi_handle handle) { int idle_nums; + struct acpi_buffer param = { + .length = 4, + .pointer = (void *)&idle_nums, + }; + mutex_lock(&xen_cpu_lock); idle_nums = acpi_pad_pur(handle); @@ -109,7 +96,8 @@ static void acpi_pad_handle_notify(acpi_handle handle) idle_nums = xen_acpi_pad_idle_cpus(idle_nums) ?: xen_acpi_pad_idle_cpus_num(); if (idle_nums >= 0) - acpi_pad_ost(handle, 0, idle_nums); + acpi_evaluate_ost(handle, ACPI_PROCESSOR_AGGREGATOR_NOTIFY, + 0, ¶m); mutex_unlock(&xen_cpu_lock); } |