diff options
author | Mark Brown <broonie@kernel.org> | 2014-10-27 07:18:55 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2014-10-27 07:18:55 -0400 |
commit | a7339126fdc6d94bc9deba0eaa751622c37b3948 (patch) | |
tree | 39b2e2c4df03471ab764c4415c00727e623ee8c3 /drivers | |
parent | 5aa664d63391c14f52264eb08d70f8a074ae249e (diff) | |
parent | cac7f2429872d3733dc3f9915857b1691da2eb2f (diff) |
Merge tag 'v3.18-rc2' into asoc-sgtl5000
Linux 3.18-rc2
Diffstat (limited to 'drivers')
158 files changed, 7011 insertions, 1298 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index d0f3265fb85d..b23fe37f67c0 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -144,7 +144,7 @@ config ACPI_VIDEO | |||
144 | 144 | ||
145 | config ACPI_FAN | 145 | config ACPI_FAN |
146 | tristate "Fan" | 146 | tristate "Fan" |
147 | select THERMAL | 147 | depends on THERMAL |
148 | default y | 148 | default y |
149 | help | 149 | help |
150 | This driver supports ACPI fan devices, allowing user-mode | 150 | This driver supports ACPI fan devices, allowing user-mode |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 505d4d79fe3e..c3b2fcb729f3 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -43,6 +43,7 @@ acpi-y += pci_root.o pci_link.o pci_irq.o | |||
43 | acpi-y += acpi_lpss.o | 43 | acpi-y += acpi_lpss.o |
44 | acpi-y += acpi_platform.o | 44 | acpi-y += acpi_platform.o |
45 | acpi-y += acpi_pnp.o | 45 | acpi-y += acpi_pnp.o |
46 | acpi-y += int340x_thermal.o | ||
46 | acpi-y += power.o | 47 | acpi-y += power.o |
47 | acpi-y += event.o | 48 | acpi-y += event.o |
48 | acpi-y += sysfs.o | 49 | acpi-y += sysfs.o |
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 2bf9082f7523..6ba8beb6b9d2 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/err.h> | 16 | #include <linux/err.h> |
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/dma-mapping.h> | ||
19 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
20 | 21 | ||
21 | #include "internal.h" | 22 | #include "internal.h" |
@@ -102,6 +103,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) | |||
102 | pdevinfo.res = resources; | 103 | pdevinfo.res = resources; |
103 | pdevinfo.num_res = count; | 104 | pdevinfo.num_res = count; |
104 | pdevinfo.acpi_node.companion = adev; | 105 | pdevinfo.acpi_node.companion = adev; |
106 | pdevinfo.dma_mask = DMA_BIT_MASK(32); | ||
105 | pdev = platform_device_register_full(&pdevinfo); | 107 | pdev = platform_device_register_full(&pdevinfo); |
106 | if (IS_ERR(pdev)) | 108 | if (IS_ERR(pdev)) |
107 | dev_err(&adev->dev, "platform device creation failed: %ld\n", | 109 | dev_err(&adev->dev, "platform device creation failed: %ld\n", |
@@ -113,3 +115,4 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) | |||
113 | kfree(resources); | 115 | kfree(resources); |
114 | return pdev; | 116 | return pdev; |
115 | } | 117 | } |
118 | EXPORT_SYMBOL_GPL(acpi_create_platform_device); | ||
diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 2ad2351a9833..c318d3e27893 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h | |||
@@ -127,7 +127,7 @@ acpi_hw_clear_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, | |||
127 | 127 | ||
128 | acpi_status | 128 | acpi_status |
129 | acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info, | 129 | acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info, |
130 | acpi_event_status * event_status); | 130 | acpi_event_status *event_status); |
131 | 131 | ||
132 | acpi_status acpi_hw_disable_all_gpes(void); | 132 | acpi_status acpi_hw_disable_all_gpes(void); |
133 | 133 | ||
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 2747279fbe3c..c00e7e41ad75 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h | |||
@@ -413,8 +413,8 @@ struct acpi_gpe_handler_info { | |||
413 | acpi_gpe_handler address; /* Address of handler, if any */ | 413 | acpi_gpe_handler address; /* Address of handler, if any */ |
414 | void *context; /* Context to be passed to handler */ | 414 | void *context; /* Context to be passed to handler */ |
415 | struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ | 415 | struct acpi_namespace_node *method_node; /* Method node for this GPE level (saved) */ |
416 | u8 original_flags; /* Original (pre-handler) GPE info */ | 416 | u8 original_flags; /* Original (pre-handler) GPE info */ |
417 | u8 originally_enabled; /* True if GPE was originally enabled */ | 417 | u8 originally_enabled; /* True if GPE was originally enabled */ |
418 | }; | 418 | }; |
419 | 419 | ||
420 | /* Notify info for implicit notify, multiple device objects */ | 420 | /* Notify info for implicit notify, multiple device objects */ |
diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index f14882788eee..1afe46e44dac 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h | |||
@@ -49,6 +49,8 @@ acpi_status acpi_allocate_root_table(u32 initial_table_count); | |||
49 | /* | 49 | /* |
50 | * tbxfroot - Root pointer utilities | 50 | * tbxfroot - Root pointer utilities |
51 | */ | 51 | */ |
52 | u32 acpi_tb_get_rsdp_length(struct acpi_table_rsdp *rsdp); | ||
53 | |||
52 | acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp); | 54 | acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp); |
53 | 55 | ||
54 | u8 *acpi_tb_scan_memory_for_rsdp(u8 *start_address, u32 length); | 56 | u8 *acpi_tb_scan_memory_for_rsdp(u8 *start_address, u32 length); |
diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h index f3f834408441..3a0beeb86ba5 100644 --- a/drivers/acpi/acpica/amlresrc.h +++ b/drivers/acpi/acpica/amlresrc.h | |||
@@ -117,6 +117,12 @@ struct asl_resource_node { | |||
117 | struct asl_resource_node *next; | 117 | struct asl_resource_node *next; |
118 | }; | 118 | }; |
119 | 119 | ||
120 | struct asl_resource_info { | ||
121 | union acpi_parse_object *descriptor_type_op; /* Resource descriptor parse node */ | ||
122 | union acpi_parse_object *mapping_op; /* Used for mapfile support */ | ||
123 | u32 current_byte_offset; /* Offset in resource template */ | ||
124 | }; | ||
125 | |||
120 | /* Macros used to generate AML resource length fields */ | 126 | /* Macros used to generate AML resource length fields */ |
121 | 127 | ||
122 | #define ACPI_AML_SIZE_LARGE(r) (sizeof (r) - sizeof (struct aml_resource_large_header)) | 128 | #define ACPI_AML_SIZE_LARGE(r) (sizeof (r) - sizeof (struct aml_resource_large_header)) |
@@ -449,4 +455,32 @@ union aml_resource { | |||
449 | u8 byte_item; | 455 | u8 byte_item; |
450 | }; | 456 | }; |
451 | 457 | ||
458 | /* Interfaces used by both the disassembler and compiler */ | ||
459 | |||
460 | void | ||
461 | mp_save_gpio_info(union acpi_parse_object *op, | ||
462 | union aml_resource *resource, | ||
463 | u32 pin_count, u16 *pin_list, char *device_name); | ||
464 | |||
465 | void | ||
466 | mp_save_serial_info(union acpi_parse_object *op, | ||
467 | union aml_resource *resource, char *device_name); | ||
468 | |||
469 | char *mp_get_hid_from_parse_tree(struct acpi_namespace_node *hid_node); | ||
470 | |||
471 | char *mp_get_hid_via_namestring(char *device_name); | ||
472 | |||
473 | char *mp_get_connection_info(union acpi_parse_object *op, | ||
474 | u32 pin_index, | ||
475 | struct acpi_namespace_node **target_node, | ||
476 | char **target_name); | ||
477 | |||
478 | char *mp_get_parent_device_hid(union acpi_parse_object *op, | ||
479 | struct acpi_namespace_node **target_node, | ||
480 | char **parent_device_name); | ||
481 | |||
482 | char *mp_get_ddn_value(char *device_name); | ||
483 | |||
484 | char *mp_get_hid_value(struct acpi_namespace_node *device_node); | ||
485 | |||
452 | #endif | 486 | #endif |
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index e4ba4dec86af..2095dfb72bcb 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c | |||
@@ -100,13 +100,14 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info) | |||
100 | * | 100 | * |
101 | * FUNCTION: acpi_ev_enable_gpe | 101 | * FUNCTION: acpi_ev_enable_gpe |
102 | * | 102 | * |
103 | * PARAMETERS: gpe_event_info - GPE to enable | 103 | * PARAMETERS: gpe_event_info - GPE to enable |
104 | * | 104 | * |
105 | * RETURN: Status | 105 | * RETURN: Status |
106 | * | 106 | * |
107 | * DESCRIPTION: Clear a GPE of stale events and enable it. | 107 | * DESCRIPTION: Clear a GPE of stale events and enable it. |
108 | * | 108 | * |
109 | ******************************************************************************/ | 109 | ******************************************************************************/ |
110 | |||
110 | acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) | 111 | acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) |
111 | { | 112 | { |
112 | acpi_status status; | 113 | acpi_status status; |
@@ -125,6 +126,7 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) | |||
125 | } | 126 | } |
126 | 127 | ||
127 | /* Clear the GPE (of stale events) */ | 128 | /* Clear the GPE (of stale events) */ |
129 | |||
128 | status = acpi_hw_clear_gpe(gpe_event_info); | 130 | status = acpi_hw_clear_gpe(gpe_event_info); |
129 | if (ACPI_FAILURE(status)) { | 131 | if (ACPI_FAILURE(status)) { |
130 | return_ACPI_STATUS(status); | 132 | return_ACPI_STATUS(status); |
@@ -136,7 +138,6 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) | |||
136 | return_ACPI_STATUS(status); | 138 | return_ACPI_STATUS(status); |
137 | } | 139 | } |
138 | 140 | ||
139 | |||
140 | /******************************************************************************* | 141 | /******************************************************************************* |
141 | * | 142 | * |
142 | * FUNCTION: acpi_ev_add_gpe_reference | 143 | * FUNCTION: acpi_ev_add_gpe_reference |
@@ -212,7 +213,7 @@ acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info) | |||
212 | if (ACPI_SUCCESS(status)) { | 213 | if (ACPI_SUCCESS(status)) { |
213 | status = | 214 | status = |
214 | acpi_hw_low_set_gpe(gpe_event_info, | 215 | acpi_hw_low_set_gpe(gpe_event_info, |
215 | ACPI_GPE_DISABLE); | 216 | ACPI_GPE_DISABLE); |
216 | } | 217 | } |
217 | 218 | ||
218 | if (ACPI_FAILURE(status)) { | 219 | if (ACPI_FAILURE(status)) { |
@@ -334,7 +335,7 @@ struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, | |||
334 | * | 335 | * |
335 | ******************************************************************************/ | 336 | ******************************************************************************/ |
336 | 337 | ||
337 | u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) | 338 | u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list) |
338 | { | 339 | { |
339 | acpi_status status; | 340 | acpi_status status; |
340 | struct acpi_gpe_block_info *gpe_block; | 341 | struct acpi_gpe_block_info *gpe_block; |
@@ -427,7 +428,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) | |||
427 | 428 | ||
428 | /* Check if there is anything active at all in this register */ | 429 | /* Check if there is anything active at all in this register */ |
429 | 430 | ||
430 | enabled_status_byte = (u8) (status_reg & enable_reg); | 431 | enabled_status_byte = (u8)(status_reg & enable_reg); |
431 | if (!enabled_status_byte) { | 432 | if (!enabled_status_byte) { |
432 | 433 | ||
433 | /* No active GPEs in this register, move on */ | 434 | /* No active GPEs in this register, move on */ |
@@ -450,7 +451,7 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info * gpe_xrupt_list) | |||
450 | acpi_ev_gpe_dispatch(gpe_block-> | 451 | acpi_ev_gpe_dispatch(gpe_block-> |
451 | node, | 452 | node, |
452 | &gpe_block-> | 453 | &gpe_block-> |
453 | event_info[((acpi_size) i * ACPI_GPE_REGISTER_WIDTH) + j], j + gpe_register_info->base_gpe_number); | 454 | event_info[((acpi_size) i * ACPI_GPE_REGISTER_WIDTH) + j], j + gpe_register_info->base_gpe_number); |
454 | } | 455 | } |
455 | } | 456 | } |
456 | } | 457 | } |
@@ -636,7 +637,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context) | |||
636 | * | 637 | * |
637 | ******************************************************************************/ | 638 | ******************************************************************************/ |
638 | 639 | ||
639 | acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info) | 640 | acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info * gpe_event_info) |
640 | { | 641 | { |
641 | acpi_status status; | 642 | acpi_status status; |
642 | 643 | ||
@@ -666,9 +667,9 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info) | |||
666 | * | 667 | * |
667 | * FUNCTION: acpi_ev_gpe_dispatch | 668 | * FUNCTION: acpi_ev_gpe_dispatch |
668 | * | 669 | * |
669 | * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 | 670 | * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 |
670 | * gpe_event_info - Info for this GPE | 671 | * gpe_event_info - Info for this GPE |
671 | * gpe_number - Number relative to the parent GPE block | 672 | * gpe_number - Number relative to the parent GPE block |
672 | * | 673 | * |
673 | * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED | 674 | * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED |
674 | * | 675 | * |
@@ -681,7 +682,7 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info) | |||
681 | 682 | ||
682 | u32 | 683 | u32 |
683 | acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, | 684 | acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, |
684 | struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) | 685 | struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) |
685 | { | 686 | { |
686 | acpi_status status; | 687 | acpi_status status; |
687 | u32 return_value; | 688 | u32 return_value; |
diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 49fc7effd961..7be928379879 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c | |||
@@ -424,6 +424,7 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, | |||
424 | } | 424 | } |
425 | 425 | ||
426 | /* Disable the GPE in case it's been enabled already. */ | 426 | /* Disable the GPE in case it's been enabled already. */ |
427 | |||
427 | (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); | 428 | (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); |
428 | 429 | ||
429 | /* | 430 | /* |
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 11e5803b8b41..55a58f3ec8df 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c | |||
@@ -786,18 +786,26 @@ acpi_install_gpe_handler(acpi_handle gpe_device, | |||
786 | handler->method_node = gpe_event_info->dispatch.method_node; | 786 | handler->method_node = gpe_event_info->dispatch.method_node; |
787 | handler->original_flags = (u8)(gpe_event_info->flags & | 787 | handler->original_flags = (u8)(gpe_event_info->flags & |
788 | (ACPI_GPE_XRUPT_TYPE_MASK | | 788 | (ACPI_GPE_XRUPT_TYPE_MASK | |
789 | ACPI_GPE_DISPATCH_MASK)); | 789 | ACPI_GPE_DISPATCH_MASK)); |
790 | 790 | ||
791 | /* | 791 | /* |
792 | * If the GPE is associated with a method, it may have been enabled | 792 | * If the GPE is associated with a method, it may have been enabled |
793 | * automatically during initialization, in which case it has to be | 793 | * automatically during initialization, in which case it has to be |
794 | * disabled now to avoid spurious execution of the handler. | 794 | * disabled now to avoid spurious execution of the handler. |
795 | */ | 795 | */ |
796 | 796 | if (((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) || | |
797 | if ((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) | 797 | (handler->original_flags & ACPI_GPE_DISPATCH_NOTIFY)) && |
798 | && gpe_event_info->runtime_count) { | 798 | gpe_event_info->runtime_count) { |
799 | handler->originally_enabled = 1; | 799 | handler->originally_enabled = TRUE; |
800 | (void)acpi_ev_remove_gpe_reference(gpe_event_info); | 800 | (void)acpi_ev_remove_gpe_reference(gpe_event_info); |
801 | |||
802 | /* Sanity check of original type against new type */ | ||
803 | |||
804 | if (type != | ||
805 | (u32)(gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK)) { | ||
806 | ACPI_WARNING((AE_INFO, | ||
807 | "GPE type mismatch (level/edge)")); | ||
808 | } | ||
801 | } | 809 | } |
802 | 810 | ||
803 | /* Install the handler */ | 811 | /* Install the handler */ |
@@ -808,7 +816,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, | |||
808 | 816 | ||
809 | gpe_event_info->flags &= | 817 | gpe_event_info->flags &= |
810 | ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); | 818 | ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); |
811 | gpe_event_info->flags |= (u8) (type | ACPI_GPE_DISPATCH_HANDLER); | 819 | gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_HANDLER); |
812 | 820 | ||
813 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); | 821 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); |
814 | 822 | ||
@@ -893,7 +901,7 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, | |||
893 | 901 | ||
894 | gpe_event_info->dispatch.method_node = handler->method_node; | 902 | gpe_event_info->dispatch.method_node = handler->method_node; |
895 | gpe_event_info->flags &= | 903 | gpe_event_info->flags &= |
896 | ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); | 904 | ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); |
897 | gpe_event_info->flags |= handler->original_flags; | 905 | gpe_event_info->flags |= handler->original_flags; |
898 | 906 | ||
899 | /* | 907 | /* |
@@ -901,7 +909,8 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, | |||
901 | * enabled, it should be enabled at this point to restore the | 909 | * enabled, it should be enabled at this point to restore the |
902 | * post-initialization configuration. | 910 | * post-initialization configuration. |
903 | */ | 911 | */ |
904 | if ((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) && | 912 | if (((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) || |
913 | (handler->original_flags & ACPI_GPE_DISPATCH_NOTIFY)) && | ||
905 | handler->originally_enabled) { | 914 | handler->originally_enabled) { |
906 | (void)acpi_ev_add_gpe_reference(gpe_event_info); | 915 | (void)acpi_ev_add_gpe_reference(gpe_event_info); |
907 | } | 916 | } |
@@ -946,7 +955,7 @@ ACPI_EXPORT_SYMBOL(acpi_remove_gpe_handler) | |||
946 | * handle is returned. | 955 | * handle is returned. |
947 | * | 956 | * |
948 | ******************************************************************************/ | 957 | ******************************************************************************/ |
949 | acpi_status acpi_acquire_global_lock(u16 timeout, u32 * handle) | 958 | acpi_status acpi_acquire_global_lock(u16 timeout, u32 *handle) |
950 | { | 959 | { |
951 | acpi_status status; | 960 | acpi_status status; |
952 | 961 | ||
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index e286640ad4ff..bb8cbf5961bf 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c | |||
@@ -324,8 +324,9 @@ ACPI_EXPORT_SYMBOL(acpi_clear_event) | |||
324 | ******************************************************************************/ | 324 | ******************************************************************************/ |
325 | acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status) | 325 | acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status) |
326 | { | 326 | { |
327 | acpi_status status = AE_OK; | 327 | acpi_status status; |
328 | u32 value; | 328 | acpi_event_status local_event_status = 0; |
329 | u32 in_byte; | ||
329 | 330 | ||
330 | ACPI_FUNCTION_TRACE(acpi_get_event_status); | 331 | ACPI_FUNCTION_TRACE(acpi_get_event_status); |
331 | 332 | ||
@@ -339,29 +340,40 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status) | |||
339 | return_ACPI_STATUS(AE_BAD_PARAMETER); | 340 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
340 | } | 341 | } |
341 | 342 | ||
342 | /* Get the status of the requested fixed event */ | 343 | /* Fixed event currently can be dispatched? */ |
344 | |||
345 | if (acpi_gbl_fixed_event_handlers[event].handler) { | ||
346 | local_event_status |= ACPI_EVENT_FLAG_HAS_HANDLER; | ||
347 | } | ||
348 | |||
349 | /* Fixed event currently enabled? */ | ||
343 | 350 | ||
344 | status = | 351 | status = |
345 | acpi_read_bit_register(acpi_gbl_fixed_event_info[event]. | 352 | acpi_read_bit_register(acpi_gbl_fixed_event_info[event]. |
346 | enable_register_id, &value); | 353 | enable_register_id, &in_byte); |
347 | if (ACPI_FAILURE(status)) | 354 | if (ACPI_FAILURE(status)) { |
348 | return_ACPI_STATUS(status); | 355 | return_ACPI_STATUS(status); |
356 | } | ||
349 | 357 | ||
350 | *event_status = value; | 358 | if (in_byte) { |
359 | local_event_status |= ACPI_EVENT_FLAG_ENABLED; | ||
360 | } | ||
361 | |||
362 | /* Fixed event currently active? */ | ||
351 | 363 | ||
352 | status = | 364 | status = |
353 | acpi_read_bit_register(acpi_gbl_fixed_event_info[event]. | 365 | acpi_read_bit_register(acpi_gbl_fixed_event_info[event]. |
354 | status_register_id, &value); | 366 | status_register_id, &in_byte); |
355 | if (ACPI_FAILURE(status)) | 367 | if (ACPI_FAILURE(status)) { |
356 | return_ACPI_STATUS(status); | 368 | return_ACPI_STATUS(status); |
369 | } | ||
357 | 370 | ||
358 | if (value) | 371 | if (in_byte) { |
359 | *event_status |= ACPI_EVENT_FLAG_SET; | 372 | local_event_status |= ACPI_EVENT_FLAG_SET; |
360 | 373 | } | |
361 | if (acpi_gbl_fixed_event_handlers[event].handler) | ||
362 | *event_status |= ACPI_EVENT_FLAG_HANDLE; | ||
363 | 374 | ||
364 | return_ACPI_STATUS(status); | 375 | (*event_status) = local_event_status; |
376 | return_ACPI_STATUS(AE_OK); | ||
365 | } | 377 | } |
366 | 378 | ||
367 | ACPI_EXPORT_SYMBOL(acpi_get_event_status) | 379 | ACPI_EXPORT_SYMBOL(acpi_get_event_status) |
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 56710a03c9b0..e889a5304abd 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c | |||
@@ -106,8 +106,8 @@ ACPI_EXPORT_SYMBOL(acpi_update_all_gpes) | |||
106 | * | 106 | * |
107 | * FUNCTION: acpi_enable_gpe | 107 | * FUNCTION: acpi_enable_gpe |
108 | * | 108 | * |
109 | * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 | 109 | * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 |
110 | * gpe_number - GPE level within the GPE block | 110 | * gpe_number - GPE level within the GPE block |
111 | * | 111 | * |
112 | * RETURN: Status | 112 | * RETURN: Status |
113 | * | 113 | * |
@@ -115,7 +115,6 @@ ACPI_EXPORT_SYMBOL(acpi_update_all_gpes) | |||
115 | * hardware-enabled. | 115 | * hardware-enabled. |
116 | * | 116 | * |
117 | ******************************************************************************/ | 117 | ******************************************************************************/ |
118 | |||
119 | acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) | 118 | acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) |
120 | { | 119 | { |
121 | acpi_status status = AE_BAD_PARAMETER; | 120 | acpi_status status = AE_BAD_PARAMETER; |
@@ -490,8 +489,8 @@ ACPI_EXPORT_SYMBOL(acpi_clear_gpe) | |||
490 | * | 489 | * |
491 | * FUNCTION: acpi_get_gpe_status | 490 | * FUNCTION: acpi_get_gpe_status |
492 | * | 491 | * |
493 | * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 | 492 | * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 |
494 | * gpe_number - GPE level within the GPE block | 493 | * gpe_number - GPE level within the GPE block |
495 | * event_status - Where the current status of the event | 494 | * event_status - Where the current status of the event |
496 | * will be returned | 495 | * will be returned |
497 | * | 496 | * |
@@ -524,9 +523,6 @@ acpi_get_gpe_status(acpi_handle gpe_device, | |||
524 | 523 | ||
525 | status = acpi_hw_get_gpe_status(gpe_event_info, event_status); | 524 | status = acpi_hw_get_gpe_status(gpe_event_info, event_status); |
526 | 525 | ||
527 | if (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) | ||
528 | *event_status |= ACPI_EVENT_FLAG_HANDLE; | ||
529 | |||
530 | unlock_and_exit: | 526 | unlock_and_exit: |
531 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); | 527 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); |
532 | return_ACPI_STATUS(status); | 528 | return_ACPI_STATUS(status); |
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index ea62d40fd161..48ac7b7b59cd 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c | |||
@@ -202,7 +202,7 @@ acpi_status acpi_hw_clear_gpe(struct acpi_gpe_event_info * gpe_event_info) | |||
202 | 202 | ||
203 | acpi_status | 203 | acpi_status |
204 | acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, | 204 | acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, |
205 | acpi_event_status * event_status) | 205 | acpi_event_status *event_status) |
206 | { | 206 | { |
207 | u32 in_byte; | 207 | u32 in_byte; |
208 | u32 register_bit; | 208 | u32 register_bit; |
@@ -216,6 +216,13 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, | |||
216 | return (AE_BAD_PARAMETER); | 216 | return (AE_BAD_PARAMETER); |
217 | } | 217 | } |
218 | 218 | ||
219 | /* GPE currently handled? */ | ||
220 | |||
221 | if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) != | ||
222 | ACPI_GPE_DISPATCH_NONE) { | ||
223 | local_event_status |= ACPI_EVENT_FLAG_HAS_HANDLER; | ||
224 | } | ||
225 | |||
219 | /* Get the info block for the entire GPE register */ | 226 | /* Get the info block for the entire GPE register */ |
220 | 227 | ||
221 | gpe_register_info = gpe_event_info->register_info; | 228 | gpe_register_info = gpe_event_info->register_info; |
diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c index 65ab8fed3d5e..43a54af2b548 100644 --- a/drivers/acpi/acpica/tbxfroot.c +++ b/drivers/acpi/acpica/tbxfroot.c | |||
@@ -50,6 +50,36 @@ ACPI_MODULE_NAME("tbxfroot") | |||
50 | 50 | ||
51 | /******************************************************************************* | 51 | /******************************************************************************* |
52 | * | 52 | * |
53 | * FUNCTION: acpi_tb_get_rsdp_length | ||
54 | * | ||
55 | * PARAMETERS: rsdp - Pointer to RSDP | ||
56 | * | ||
57 | * RETURN: Table length | ||
58 | * | ||
59 | * DESCRIPTION: Get the length of the RSDP | ||
60 | * | ||
61 | ******************************************************************************/ | ||
62 | u32 acpi_tb_get_rsdp_length(struct acpi_table_rsdp *rsdp) | ||
63 | { | ||
64 | |||
65 | if (!ACPI_VALIDATE_RSDP_SIG(rsdp->signature)) { | ||
66 | |||
67 | /* BAD Signature */ | ||
68 | |||
69 | return (0); | ||
70 | } | ||
71 | |||
72 | /* "Length" field is available if table version >= 2 */ | ||
73 | |||
74 | if (rsdp->revision >= 2) { | ||
75 | return (rsdp->length); | ||
76 | } else { | ||
77 | return (ACPI_RSDP_CHECKSUM_LENGTH); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | /******************************************************************************* | ||
82 | * | ||
53 | * FUNCTION: acpi_tb_validate_rsdp | 83 | * FUNCTION: acpi_tb_validate_rsdp |
54 | * | 84 | * |
55 | * PARAMETERS: rsdp - Pointer to unvalidated RSDP | 85 | * PARAMETERS: rsdp - Pointer to unvalidated RSDP |
@@ -59,7 +89,8 @@ ACPI_MODULE_NAME("tbxfroot") | |||
59 | * DESCRIPTION: Validate the RSDP (ptr) | 89 | * DESCRIPTION: Validate the RSDP (ptr) |
60 | * | 90 | * |
61 | ******************************************************************************/ | 91 | ******************************************************************************/ |
62 | acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp) | 92 | |
93 | acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp * rsdp) | ||
63 | { | 94 | { |
64 | 95 | ||
65 | /* | 96 | /* |
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index bea6896be122..143ec6ea1468 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c | |||
@@ -343,6 +343,7 @@ int acpi_device_update_power(struct acpi_device *device, int *state_p) | |||
343 | 343 | ||
344 | return 0; | 344 | return 0; |
345 | } | 345 | } |
346 | EXPORT_SYMBOL_GPL(acpi_device_update_power); | ||
346 | 347 | ||
347 | int acpi_bus_update_power(acpi_handle handle, int *state_p) | 348 | int acpi_bus_update_power(acpi_handle handle, int *state_p) |
348 | { | 349 | { |
@@ -710,7 +711,7 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) | |||
710 | return -ENODEV; | 711 | return -ENODEV; |
711 | } | 712 | } |
712 | 713 | ||
713 | return acpi_device_wakeup(adev, enable, ACPI_STATE_S0); | 714 | return acpi_device_wakeup(adev, ACPI_STATE_S0, enable); |
714 | } | 715 | } |
715 | EXPORT_SYMBOL(acpi_pm_device_run_wake); | 716 | EXPORT_SYMBOL(acpi_pm_device_run_wake); |
716 | #endif /* CONFIG_PM_RUNTIME */ | 717 | #endif /* CONFIG_PM_RUNTIME */ |
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index cb6066c809ea..3d304ff7f095 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -128,12 +128,13 @@ static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ | |||
128 | static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ | 128 | static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ |
129 | 129 | ||
130 | /* -------------------------------------------------------------------------- | 130 | /* -------------------------------------------------------------------------- |
131 | Transaction Management | 131 | * Transaction Management |
132 | -------------------------------------------------------------------------- */ | 132 | * -------------------------------------------------------------------------- */ |
133 | 133 | ||
134 | static inline u8 acpi_ec_read_status(struct acpi_ec *ec) | 134 | static inline u8 acpi_ec_read_status(struct acpi_ec *ec) |
135 | { | 135 | { |
136 | u8 x = inb(ec->command_addr); | 136 | u8 x = inb(ec->command_addr); |
137 | |||
137 | pr_debug("EC_SC(R) = 0x%2.2x " | 138 | pr_debug("EC_SC(R) = 0x%2.2x " |
138 | "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d\n", | 139 | "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d\n", |
139 | x, | 140 | x, |
@@ -148,6 +149,7 @@ static inline u8 acpi_ec_read_status(struct acpi_ec *ec) | |||
148 | static inline u8 acpi_ec_read_data(struct acpi_ec *ec) | 149 | static inline u8 acpi_ec_read_data(struct acpi_ec *ec) |
149 | { | 150 | { |
150 | u8 x = inb(ec->data_addr); | 151 | u8 x = inb(ec->data_addr); |
152 | |||
151 | pr_debug("EC_DATA(R) = 0x%2.2x\n", x); | 153 | pr_debug("EC_DATA(R) = 0x%2.2x\n", x); |
152 | return x; | 154 | return x; |
153 | } | 155 | } |
@@ -164,10 +166,32 @@ static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) | |||
164 | outb(data, ec->data_addr); | 166 | outb(data, ec->data_addr); |
165 | } | 167 | } |
166 | 168 | ||
169 | #ifdef DEBUG | ||
170 | static const char *acpi_ec_cmd_string(u8 cmd) | ||
171 | { | ||
172 | switch (cmd) { | ||
173 | case 0x80: | ||
174 | return "RD_EC"; | ||
175 | case 0x81: | ||
176 | return "WR_EC"; | ||
177 | case 0x82: | ||
178 | return "BE_EC"; | ||
179 | case 0x83: | ||
180 | return "BD_EC"; | ||
181 | case 0x84: | ||
182 | return "QR_EC"; | ||
183 | } | ||
184 | return "UNKNOWN"; | ||
185 | } | ||
186 | #else | ||
187 | #define acpi_ec_cmd_string(cmd) "UNDEF" | ||
188 | #endif | ||
189 | |||
167 | static int ec_transaction_completed(struct acpi_ec *ec) | 190 | static int ec_transaction_completed(struct acpi_ec *ec) |
168 | { | 191 | { |
169 | unsigned long flags; | 192 | unsigned long flags; |
170 | int ret = 0; | 193 | int ret = 0; |
194 | |||
171 | spin_lock_irqsave(&ec->lock, flags); | 195 | spin_lock_irqsave(&ec->lock, flags); |
172 | if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE)) | 196 | if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE)) |
173 | ret = 1; | 197 | ret = 1; |
@@ -181,7 +205,8 @@ static bool advance_transaction(struct acpi_ec *ec) | |||
181 | u8 status; | 205 | u8 status; |
182 | bool wakeup = false; | 206 | bool wakeup = false; |
183 | 207 | ||
184 | pr_debug("===== %s =====\n", in_interrupt() ? "IRQ" : "TASK"); | 208 | pr_debug("===== %s (%d) =====\n", |
209 | in_interrupt() ? "IRQ" : "TASK", smp_processor_id()); | ||
185 | status = acpi_ec_read_status(ec); | 210 | status = acpi_ec_read_status(ec); |
186 | t = ec->curr; | 211 | t = ec->curr; |
187 | if (!t) | 212 | if (!t) |
@@ -198,7 +223,8 @@ static bool advance_transaction(struct acpi_ec *ec) | |||
198 | if (t->rlen == t->ri) { | 223 | if (t->rlen == t->ri) { |
199 | t->flags |= ACPI_EC_COMMAND_COMPLETE; | 224 | t->flags |= ACPI_EC_COMMAND_COMPLETE; |
200 | if (t->command == ACPI_EC_COMMAND_QUERY) | 225 | if (t->command == ACPI_EC_COMMAND_QUERY) |
201 | pr_debug("hardware QR_EC completion\n"); | 226 | pr_debug("***** Command(%s) hardware completion *****\n", |
227 | acpi_ec_cmd_string(t->command)); | ||
202 | wakeup = true; | 228 | wakeup = true; |
203 | } | 229 | } |
204 | } else | 230 | } else |
@@ -221,7 +247,8 @@ static bool advance_transaction(struct acpi_ec *ec) | |||
221 | t->flags |= ACPI_EC_COMMAND_POLL; | 247 | t->flags |= ACPI_EC_COMMAND_POLL; |
222 | t->rdata[t->ri++] = 0x00; | 248 | t->rdata[t->ri++] = 0x00; |
223 | t->flags |= ACPI_EC_COMMAND_COMPLETE; | 249 | t->flags |= ACPI_EC_COMMAND_COMPLETE; |
224 | pr_debug("software QR_EC completion\n"); | 250 | pr_debug("***** Command(%s) software completion *****\n", |
251 | acpi_ec_cmd_string(t->command)); | ||
225 | wakeup = true; | 252 | wakeup = true; |
226 | } else if ((status & ACPI_EC_FLAG_IBF) == 0) { | 253 | } else if ((status & ACPI_EC_FLAG_IBF) == 0) { |
227 | acpi_ec_write_cmd(ec, t->command); | 254 | acpi_ec_write_cmd(ec, t->command); |
@@ -264,6 +291,7 @@ static int ec_poll(struct acpi_ec *ec) | |||
264 | { | 291 | { |
265 | unsigned long flags; | 292 | unsigned long flags; |
266 | int repeat = 5; /* number of command restarts */ | 293 | int repeat = 5; /* number of command restarts */ |
294 | |||
267 | while (repeat--) { | 295 | while (repeat--) { |
268 | unsigned long delay = jiffies + | 296 | unsigned long delay = jiffies + |
269 | msecs_to_jiffies(ec_delay); | 297 | msecs_to_jiffies(ec_delay); |
@@ -296,18 +324,25 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, | |||
296 | { | 324 | { |
297 | unsigned long tmp; | 325 | unsigned long tmp; |
298 | int ret = 0; | 326 | int ret = 0; |
327 | |||
299 | if (EC_FLAGS_MSI) | 328 | if (EC_FLAGS_MSI) |
300 | udelay(ACPI_EC_MSI_UDELAY); | 329 | udelay(ACPI_EC_MSI_UDELAY); |
301 | /* start transaction */ | 330 | /* start transaction */ |
302 | spin_lock_irqsave(&ec->lock, tmp); | 331 | spin_lock_irqsave(&ec->lock, tmp); |
303 | /* following two actions should be kept atomic */ | 332 | /* following two actions should be kept atomic */ |
304 | ec->curr = t; | 333 | ec->curr = t; |
334 | pr_debug("***** Command(%s) started *****\n", | ||
335 | acpi_ec_cmd_string(t->command)); | ||
305 | start_transaction(ec); | 336 | start_transaction(ec); |
306 | spin_unlock_irqrestore(&ec->lock, tmp); | 337 | spin_unlock_irqrestore(&ec->lock, tmp); |
307 | ret = ec_poll(ec); | 338 | ret = ec_poll(ec); |
308 | spin_lock_irqsave(&ec->lock, tmp); | 339 | spin_lock_irqsave(&ec->lock, tmp); |
309 | if (ec->curr->command == ACPI_EC_COMMAND_QUERY) | 340 | if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { |
310 | clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); | 341 | clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); |
342 | pr_debug("***** Event stopped *****\n"); | ||
343 | } | ||
344 | pr_debug("***** Command(%s) stopped *****\n", | ||
345 | acpi_ec_cmd_string(t->command)); | ||
311 | ec->curr = NULL; | 346 | ec->curr = NULL; |
312 | spin_unlock_irqrestore(&ec->lock, tmp); | 347 | spin_unlock_irqrestore(&ec->lock, tmp); |
313 | return ret; | 348 | return ret; |
@@ -317,6 +352,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) | |||
317 | { | 352 | { |
318 | int status; | 353 | int status; |
319 | u32 glk; | 354 | u32 glk; |
355 | |||
320 | if (!ec || (!t) || (t->wlen && !t->wdata) || (t->rlen && !t->rdata)) | 356 | if (!ec || (!t) || (t->wlen && !t->wdata) || (t->rlen && !t->rdata)) |
321 | return -EINVAL; | 357 | return -EINVAL; |
322 | if (t->rdata) | 358 | if (t->rdata) |
@@ -333,8 +369,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) | |||
333 | goto unlock; | 369 | goto unlock; |
334 | } | 370 | } |
335 | } | 371 | } |
336 | pr_debug("transaction start (cmd=0x%02x, addr=0x%02x)\n", | ||
337 | t->command, t->wdata ? t->wdata[0] : 0); | ||
338 | /* disable GPE during transaction if storm is detected */ | 372 | /* disable GPE during transaction if storm is detected */ |
339 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | 373 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { |
340 | /* It has to be disabled, so that it doesn't trigger. */ | 374 | /* It has to be disabled, so that it doesn't trigger. */ |
@@ -355,7 +389,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) | |||
355 | t->irq_count); | 389 | t->irq_count); |
356 | set_bit(EC_FLAGS_GPE_STORM, &ec->flags); | 390 | set_bit(EC_FLAGS_GPE_STORM, &ec->flags); |
357 | } | 391 | } |
358 | pr_debug("transaction end\n"); | ||
359 | if (ec->global_lock) | 392 | if (ec->global_lock) |
360 | acpi_release_global_lock(glk); | 393 | acpi_release_global_lock(glk); |
361 | unlock: | 394 | unlock: |
@@ -383,7 +416,7 @@ static int acpi_ec_burst_disable(struct acpi_ec *ec) | |||
383 | acpi_ec_transaction(ec, &t) : 0; | 416 | acpi_ec_transaction(ec, &t) : 0; |
384 | } | 417 | } |
385 | 418 | ||
386 | static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) | 419 | static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 *data) |
387 | { | 420 | { |
388 | int result; | 421 | int result; |
389 | u8 d; | 422 | u8 d; |
@@ -419,10 +452,9 @@ int ec_read(u8 addr, u8 *val) | |||
419 | if (!err) { | 452 | if (!err) { |
420 | *val = temp_data; | 453 | *val = temp_data; |
421 | return 0; | 454 | return 0; |
422 | } else | 455 | } |
423 | return err; | 456 | return err; |
424 | } | 457 | } |
425 | |||
426 | EXPORT_SYMBOL(ec_read); | 458 | EXPORT_SYMBOL(ec_read); |
427 | 459 | ||
428 | int ec_write(u8 addr, u8 val) | 460 | int ec_write(u8 addr, u8 val) |
@@ -436,22 +468,21 @@ int ec_write(u8 addr, u8 val) | |||
436 | 468 | ||
437 | return err; | 469 | return err; |
438 | } | 470 | } |
439 | |||
440 | EXPORT_SYMBOL(ec_write); | 471 | EXPORT_SYMBOL(ec_write); |
441 | 472 | ||
442 | int ec_transaction(u8 command, | 473 | int ec_transaction(u8 command, |
443 | const u8 * wdata, unsigned wdata_len, | 474 | const u8 *wdata, unsigned wdata_len, |
444 | u8 * rdata, unsigned rdata_len) | 475 | u8 *rdata, unsigned rdata_len) |
445 | { | 476 | { |
446 | struct transaction t = {.command = command, | 477 | struct transaction t = {.command = command, |
447 | .wdata = wdata, .rdata = rdata, | 478 | .wdata = wdata, .rdata = rdata, |
448 | .wlen = wdata_len, .rlen = rdata_len}; | 479 | .wlen = wdata_len, .rlen = rdata_len}; |
480 | |||
449 | if (!first_ec) | 481 | if (!first_ec) |
450 | return -ENODEV; | 482 | return -ENODEV; |
451 | 483 | ||
452 | return acpi_ec_transaction(first_ec, &t); | 484 | return acpi_ec_transaction(first_ec, &t); |
453 | } | 485 | } |
454 | |||
455 | EXPORT_SYMBOL(ec_transaction); | 486 | EXPORT_SYMBOL(ec_transaction); |
456 | 487 | ||
457 | /* Get the handle to the EC device */ | 488 | /* Get the handle to the EC device */ |
@@ -461,7 +492,6 @@ acpi_handle ec_get_handle(void) | |||
461 | return NULL; | 492 | return NULL; |
462 | return first_ec->handle; | 493 | return first_ec->handle; |
463 | } | 494 | } |
464 | |||
465 | EXPORT_SYMBOL(ec_get_handle); | 495 | EXPORT_SYMBOL(ec_get_handle); |
466 | 496 | ||
467 | /* | 497 | /* |
@@ -525,13 +555,14 @@ void acpi_ec_unblock_transactions_early(void) | |||
525 | clear_bit(EC_FLAGS_BLOCKED, &first_ec->flags); | 555 | clear_bit(EC_FLAGS_BLOCKED, &first_ec->flags); |
526 | } | 556 | } |
527 | 557 | ||
528 | static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 * data) | 558 | static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 *data) |
529 | { | 559 | { |
530 | int result; | 560 | int result; |
531 | u8 d; | 561 | u8 d; |
532 | struct transaction t = {.command = ACPI_EC_COMMAND_QUERY, | 562 | struct transaction t = {.command = ACPI_EC_COMMAND_QUERY, |
533 | .wdata = NULL, .rdata = &d, | 563 | .wdata = NULL, .rdata = &d, |
534 | .wlen = 0, .rlen = 1}; | 564 | .wlen = 0, .rlen = 1}; |
565 | |||
535 | if (!ec || !data) | 566 | if (!ec || !data) |
536 | return -EINVAL; | 567 | return -EINVAL; |
537 | /* | 568 | /* |
@@ -557,6 +588,7 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, | |||
557 | { | 588 | { |
558 | struct acpi_ec_query_handler *handler = | 589 | struct acpi_ec_query_handler *handler = |
559 | kzalloc(sizeof(struct acpi_ec_query_handler), GFP_KERNEL); | 590 | kzalloc(sizeof(struct acpi_ec_query_handler), GFP_KERNEL); |
591 | |||
560 | if (!handler) | 592 | if (!handler) |
561 | return -ENOMEM; | 593 | return -ENOMEM; |
562 | 594 | ||
@@ -569,12 +601,12 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, | |||
569 | mutex_unlock(&ec->mutex); | 601 | mutex_unlock(&ec->mutex); |
570 | return 0; | 602 | return 0; |
571 | } | 603 | } |
572 | |||
573 | EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); | 604 | EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); |
574 | 605 | ||
575 | void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) | 606 | void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) |
576 | { | 607 | { |
577 | struct acpi_ec_query_handler *handler, *tmp; | 608 | struct acpi_ec_query_handler *handler, *tmp; |
609 | |||
578 | mutex_lock(&ec->mutex); | 610 | mutex_lock(&ec->mutex); |
579 | list_for_each_entry_safe(handler, tmp, &ec->list, node) { | 611 | list_for_each_entry_safe(handler, tmp, &ec->list, node) { |
580 | if (query_bit == handler->query_bit) { | 612 | if (query_bit == handler->query_bit) { |
@@ -584,20 +616,20 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) | |||
584 | } | 616 | } |
585 | mutex_unlock(&ec->mutex); | 617 | mutex_unlock(&ec->mutex); |
586 | } | 618 | } |
587 | |||
588 | EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); | 619 | EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); |
589 | 620 | ||
590 | static void acpi_ec_run(void *cxt) | 621 | static void acpi_ec_run(void *cxt) |
591 | { | 622 | { |
592 | struct acpi_ec_query_handler *handler = cxt; | 623 | struct acpi_ec_query_handler *handler = cxt; |
624 | |||
593 | if (!handler) | 625 | if (!handler) |
594 | return; | 626 | return; |
595 | pr_debug("start query execution\n"); | 627 | pr_debug("##### Query(0x%02x) started #####\n", handler->query_bit); |
596 | if (handler->func) | 628 | if (handler->func) |
597 | handler->func(handler->data); | 629 | handler->func(handler->data); |
598 | else if (handler->handle) | 630 | else if (handler->handle) |
599 | acpi_evaluate_object(handler->handle, NULL, NULL, NULL); | 631 | acpi_evaluate_object(handler->handle, NULL, NULL, NULL); |
600 | pr_debug("stop query execution\n"); | 632 | pr_debug("##### Query(0x%02x) stopped #####\n", handler->query_bit); |
601 | kfree(handler); | 633 | kfree(handler); |
602 | } | 634 | } |
603 | 635 | ||
@@ -620,8 +652,8 @@ static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data) | |||
620 | if (!copy) | 652 | if (!copy) |
621 | return -ENOMEM; | 653 | return -ENOMEM; |
622 | memcpy(copy, handler, sizeof(*copy)); | 654 | memcpy(copy, handler, sizeof(*copy)); |
623 | pr_debug("push query execution (0x%2x) on queue\n", | 655 | pr_debug("##### Query(0x%02x) scheduled #####\n", |
624 | value); | 656 | handler->query_bit); |
625 | return acpi_os_execute((copy->func) ? | 657 | return acpi_os_execute((copy->func) ? |
626 | OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER, | 658 | OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER, |
627 | acpi_ec_run, copy); | 659 | acpi_ec_run, copy); |
@@ -633,6 +665,7 @@ static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data) | |||
633 | static void acpi_ec_gpe_query(void *ec_cxt) | 665 | static void acpi_ec_gpe_query(void *ec_cxt) |
634 | { | 666 | { |
635 | struct acpi_ec *ec = ec_cxt; | 667 | struct acpi_ec *ec = ec_cxt; |
668 | |||
636 | if (!ec) | 669 | if (!ec) |
637 | return; | 670 | return; |
638 | mutex_lock(&ec->mutex); | 671 | mutex_lock(&ec->mutex); |
@@ -644,7 +677,7 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state) | |||
644 | { | 677 | { |
645 | if (state & ACPI_EC_FLAG_SCI) { | 678 | if (state & ACPI_EC_FLAG_SCI) { |
646 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { | 679 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { |
647 | pr_debug("push gpe query to the queue\n"); | 680 | pr_debug("***** Event started *****\n"); |
648 | return acpi_os_execute(OSL_NOTIFY_HANDLER, | 681 | return acpi_os_execute(OSL_NOTIFY_HANDLER, |
649 | acpi_ec_gpe_query, ec); | 682 | acpi_ec_gpe_query, ec); |
650 | } | 683 | } |
@@ -667,8 +700,8 @@ static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, | |||
667 | } | 700 | } |
668 | 701 | ||
669 | /* -------------------------------------------------------------------------- | 702 | /* -------------------------------------------------------------------------- |
670 | Address Space Management | 703 | * Address Space Management |
671 | -------------------------------------------------------------------------- */ | 704 | * -------------------------------------------------------------------------- */ |
672 | 705 | ||
673 | static acpi_status | 706 | static acpi_status |
674 | acpi_ec_space_handler(u32 function, acpi_physical_address address, | 707 | acpi_ec_space_handler(u32 function, acpi_physical_address address, |
@@ -699,27 +732,26 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, | |||
699 | switch (result) { | 732 | switch (result) { |
700 | case -EINVAL: | 733 | case -EINVAL: |
701 | return AE_BAD_PARAMETER; | 734 | return AE_BAD_PARAMETER; |
702 | break; | ||
703 | case -ENODEV: | 735 | case -ENODEV: |
704 | return AE_NOT_FOUND; | 736 | return AE_NOT_FOUND; |
705 | break; | ||
706 | case -ETIME: | 737 | case -ETIME: |
707 | return AE_TIME; | 738 | return AE_TIME; |
708 | break; | ||
709 | default: | 739 | default: |
710 | return AE_OK; | 740 | return AE_OK; |
711 | } | 741 | } |
712 | } | 742 | } |
713 | 743 | ||
714 | /* -------------------------------------------------------------------------- | 744 | /* -------------------------------------------------------------------------- |
715 | Driver Interface | 745 | * Driver Interface |
716 | -------------------------------------------------------------------------- */ | 746 | * -------------------------------------------------------------------------- */ |
747 | |||
717 | static acpi_status | 748 | static acpi_status |
718 | ec_parse_io_ports(struct acpi_resource *resource, void *context); | 749 | ec_parse_io_ports(struct acpi_resource *resource, void *context); |
719 | 750 | ||
720 | static struct acpi_ec *make_acpi_ec(void) | 751 | static struct acpi_ec *make_acpi_ec(void) |
721 | { | 752 | { |
722 | struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); | 753 | struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); |
754 | |||
723 | if (!ec) | 755 | if (!ec) |
724 | return NULL; | 756 | return NULL; |
725 | ec->flags = 1 << EC_FLAGS_QUERY_PENDING; | 757 | ec->flags = 1 << EC_FLAGS_QUERY_PENDING; |
@@ -742,9 +774,8 @@ acpi_ec_register_query_methods(acpi_handle handle, u32 level, | |||
742 | 774 | ||
743 | status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); | 775 | status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); |
744 | 776 | ||
745 | if (ACPI_SUCCESS(status) && sscanf(node_name, "_Q%x", &value) == 1) { | 777 | if (ACPI_SUCCESS(status) && sscanf(node_name, "_Q%x", &value) == 1) |
746 | acpi_ec_add_query_handler(ec, value, handle, NULL, NULL); | 778 | acpi_ec_add_query_handler(ec, value, handle, NULL, NULL); |
747 | } | ||
748 | return AE_OK; | 779 | return AE_OK; |
749 | } | 780 | } |
750 | 781 | ||
@@ -753,7 +784,6 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) | |||
753 | { | 784 | { |
754 | acpi_status status; | 785 | acpi_status status; |
755 | unsigned long long tmp = 0; | 786 | unsigned long long tmp = 0; |
756 | |||
757 | struct acpi_ec *ec = context; | 787 | struct acpi_ec *ec = context; |
758 | 788 | ||
759 | /* clear addr values, ec_parse_io_ports depend on it */ | 789 | /* clear addr values, ec_parse_io_ports depend on it */ |
@@ -781,6 +811,7 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) | |||
781 | static int ec_install_handlers(struct acpi_ec *ec) | 811 | static int ec_install_handlers(struct acpi_ec *ec) |
782 | { | 812 | { |
783 | acpi_status status; | 813 | acpi_status status; |
814 | |||
784 | if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) | 815 | if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) |
785 | return 0; | 816 | return 0; |
786 | status = acpi_install_gpe_handler(NULL, ec->gpe, | 817 | status = acpi_install_gpe_handler(NULL, ec->gpe, |
@@ -1078,7 +1109,8 @@ int __init acpi_ec_ecdt_probe(void) | |||
1078 | boot_ec->data_addr = ecdt_ptr->data.address; | 1109 | boot_ec->data_addr = ecdt_ptr->data.address; |
1079 | boot_ec->gpe = ecdt_ptr->gpe; | 1110 | boot_ec->gpe = ecdt_ptr->gpe; |
1080 | boot_ec->handle = ACPI_ROOT_OBJECT; | 1111 | boot_ec->handle = ACPI_ROOT_OBJECT; |
1081 | acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, &boot_ec->handle); | 1112 | acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, |
1113 | &boot_ec->handle); | ||
1082 | /* Don't trust ECDT, which comes from ASUSTek */ | 1114 | /* Don't trust ECDT, which comes from ASUSTek */ |
1083 | if (!EC_FLAGS_VALIDATE_ECDT) | 1115 | if (!EC_FLAGS_VALIDATE_ECDT) |
1084 | goto install; | 1116 | goto install; |
@@ -1162,6 +1194,5 @@ static void __exit acpi_ec_exit(void) | |||
1162 | { | 1194 | { |
1163 | 1195 | ||
1164 | acpi_bus_unregister_driver(&acpi_ec_driver); | 1196 | acpi_bus_unregister_driver(&acpi_ec_driver); |
1165 | return; | ||
1166 | } | 1197 | } |
1167 | #endif /* 0 */ | 1198 | #endif /* 0 */ |
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 5328b1090e08..caf9b76b7ef8 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c | |||
@@ -30,22 +30,19 @@ | |||
30 | #include <linux/uaccess.h> | 30 | #include <linux/uaccess.h> |
31 | #include <linux/thermal.h> | 31 | #include <linux/thermal.h> |
32 | #include <linux/acpi.h> | 32 | #include <linux/acpi.h> |
33 | 33 | #include <linux/platform_device.h> | |
34 | #define ACPI_FAN_CLASS "fan" | 34 | #include <linux/sort.h> |
35 | #define ACPI_FAN_FILE_STATE "state" | ||
36 | |||
37 | #define _COMPONENT ACPI_FAN_COMPONENT | ||
38 | ACPI_MODULE_NAME("fan"); | ||
39 | 35 | ||
40 | MODULE_AUTHOR("Paul Diefenbaugh"); | 36 | MODULE_AUTHOR("Paul Diefenbaugh"); |
41 | MODULE_DESCRIPTION("ACPI Fan Driver"); | 37 | MODULE_DESCRIPTION("ACPI Fan Driver"); |
42 | MODULE_LICENSE("GPL"); | 38 | MODULE_LICENSE("GPL"); |
43 | 39 | ||
44 | static int acpi_fan_add(struct acpi_device *device); | 40 | static int acpi_fan_probe(struct platform_device *pdev); |
45 | static int acpi_fan_remove(struct acpi_device *device); | 41 | static int acpi_fan_remove(struct platform_device *pdev); |
46 | 42 | ||
47 | static const struct acpi_device_id fan_device_ids[] = { | 43 | static const struct acpi_device_id fan_device_ids[] = { |
48 | {"PNP0C0B", 0}, | 44 | {"PNP0C0B", 0}, |
45 | {"INT3404", 0}, | ||
49 | {"", 0}, | 46 | {"", 0}, |
50 | }; | 47 | }; |
51 | MODULE_DEVICE_TABLE(acpi, fan_device_ids); | 48 | MODULE_DEVICE_TABLE(acpi, fan_device_ids); |
@@ -64,37 +61,100 @@ static struct dev_pm_ops acpi_fan_pm = { | |||
64 | #define FAN_PM_OPS_PTR NULL | 61 | #define FAN_PM_OPS_PTR NULL |
65 | #endif | 62 | #endif |
66 | 63 | ||
67 | static struct acpi_driver acpi_fan_driver = { | 64 | struct acpi_fan_fps { |
68 | .name = "fan", | 65 | u64 control; |
69 | .class = ACPI_FAN_CLASS, | 66 | u64 trip_point; |
70 | .ids = fan_device_ids, | 67 | u64 speed; |
71 | .ops = { | 68 | u64 noise_level; |
72 | .add = acpi_fan_add, | 69 | u64 power; |
73 | .remove = acpi_fan_remove, | 70 | }; |
74 | }, | 71 | |
75 | .drv.pm = FAN_PM_OPS_PTR, | 72 | struct acpi_fan_fif { |
73 | u64 revision; | ||
74 | u64 fine_grain_ctrl; | ||
75 | u64 step_size; | ||
76 | u64 low_speed_notification; | ||
77 | }; | ||
78 | |||
79 | struct acpi_fan { | ||
80 | bool acpi4; | ||
81 | struct acpi_fan_fif fif; | ||
82 | struct acpi_fan_fps *fps; | ||
83 | int fps_count; | ||
84 | struct thermal_cooling_device *cdev; | ||
85 | }; | ||
86 | |||
87 | static struct platform_driver acpi_fan_driver = { | ||
88 | .probe = acpi_fan_probe, | ||
89 | .remove = acpi_fan_remove, | ||
90 | .driver = { | ||
91 | .name = "acpi-fan", | ||
92 | .acpi_match_table = fan_device_ids, | ||
93 | .pm = FAN_PM_OPS_PTR, | ||
94 | }, | ||
76 | }; | 95 | }; |
77 | 96 | ||
78 | /* thermal cooling device callbacks */ | 97 | /* thermal cooling device callbacks */ |
79 | static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long | 98 | static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long |
80 | *state) | 99 | *state) |
81 | { | 100 | { |
82 | /* ACPI fan device only support two states: ON/OFF */ | 101 | struct acpi_device *device = cdev->devdata; |
83 | *state = 1; | 102 | struct acpi_fan *fan = acpi_driver_data(device); |
103 | |||
104 | if (fan->acpi4) | ||
105 | *state = fan->fps_count - 1; | ||
106 | else | ||
107 | *state = 1; | ||
84 | return 0; | 108 | return 0; |
85 | } | 109 | } |
86 | 110 | ||
87 | static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long | 111 | static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) |
88 | *state) | 112 | { |
113 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
114 | struct acpi_fan *fan = acpi_driver_data(device); | ||
115 | union acpi_object *obj; | ||
116 | acpi_status status; | ||
117 | int control, i; | ||
118 | |||
119 | status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer); | ||
120 | if (ACPI_FAILURE(status)) { | ||
121 | dev_err(&device->dev, "Get fan state failed\n"); | ||
122 | return status; | ||
123 | } | ||
124 | |||
125 | obj = buffer.pointer; | ||
126 | if (!obj || obj->type != ACPI_TYPE_PACKAGE || | ||
127 | obj->package.count != 3 || | ||
128 | obj->package.elements[1].type != ACPI_TYPE_INTEGER) { | ||
129 | dev_err(&device->dev, "Invalid _FST data\n"); | ||
130 | status = -EINVAL; | ||
131 | goto err; | ||
132 | } | ||
133 | |||
134 | control = obj->package.elements[1].integer.value; | ||
135 | for (i = 0; i < fan->fps_count; i++) { | ||
136 | if (control == fan->fps[i].control) | ||
137 | break; | ||
138 | } | ||
139 | if (i == fan->fps_count) { | ||
140 | dev_dbg(&device->dev, "Invalid control value returned\n"); | ||
141 | status = -EINVAL; | ||
142 | goto err; | ||
143 | } | ||
144 | |||
145 | *state = i; | ||
146 | |||
147 | err: | ||
148 | kfree(obj); | ||
149 | return status; | ||
150 | } | ||
151 | |||
152 | static int fan_get_state(struct acpi_device *device, unsigned long *state) | ||
89 | { | 153 | { |
90 | struct acpi_device *device = cdev->devdata; | ||
91 | int result; | 154 | int result; |
92 | int acpi_state = ACPI_STATE_D0; | 155 | int acpi_state = ACPI_STATE_D0; |
93 | 156 | ||
94 | if (!device) | 157 | result = acpi_device_update_power(device, &acpi_state); |
95 | return -EINVAL; | ||
96 | |||
97 | result = acpi_bus_update_power(device->handle, &acpi_state); | ||
98 | if (result) | 158 | if (result) |
99 | return result; | 159 | return result; |
100 | 160 | ||
@@ -103,21 +163,57 @@ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long | |||
103 | return 0; | 163 | return 0; |
104 | } | 164 | } |
105 | 165 | ||
106 | static int | 166 | static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long |
107 | fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) | 167 | *state) |
108 | { | 168 | { |
109 | struct acpi_device *device = cdev->devdata; | 169 | struct acpi_device *device = cdev->devdata; |
110 | int result; | 170 | struct acpi_fan *fan = acpi_driver_data(device); |
111 | 171 | ||
112 | if (!device || (state != 0 && state != 1)) | 172 | if (fan->acpi4) |
173 | return fan_get_state_acpi4(device, state); | ||
174 | else | ||
175 | return fan_get_state(device, state); | ||
176 | } | ||
177 | |||
178 | static int fan_set_state(struct acpi_device *device, unsigned long state) | ||
179 | { | ||
180 | if (state != 0 && state != 1) | ||
113 | return -EINVAL; | 181 | return -EINVAL; |
114 | 182 | ||
115 | result = acpi_bus_set_power(device->handle, | 183 | return acpi_device_set_power(device, |
116 | state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); | 184 | state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); |
185 | } | ||
117 | 186 | ||
118 | return result; | 187 | static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state) |
188 | { | ||
189 | struct acpi_fan *fan = acpi_driver_data(device); | ||
190 | acpi_status status; | ||
191 | |||
192 | if (state >= fan->fps_count) | ||
193 | return -EINVAL; | ||
194 | |||
195 | status = acpi_execute_simple_method(device->handle, "_FSL", | ||
196 | fan->fps[state].control); | ||
197 | if (ACPI_FAILURE(status)) { | ||
198 | dev_dbg(&device->dev, "Failed to set state by _FSL\n"); | ||
199 | return status; | ||
200 | } | ||
201 | |||
202 | return 0; | ||
119 | } | 203 | } |
120 | 204 | ||
205 | static int | ||
206 | fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) | ||
207 | { | ||
208 | struct acpi_device *device = cdev->devdata; | ||
209 | struct acpi_fan *fan = acpi_driver_data(device); | ||
210 | |||
211 | if (fan->acpi4) | ||
212 | return fan_set_state_acpi4(device, state); | ||
213 | else | ||
214 | return fan_set_state(device, state); | ||
215 | } | ||
216 | |||
121 | static const struct thermal_cooling_device_ops fan_cooling_ops = { | 217 | static const struct thermal_cooling_device_ops fan_cooling_ops = { |
122 | .get_max_state = fan_get_max_state, | 218 | .get_max_state = fan_get_max_state, |
123 | .get_cur_state = fan_get_cur_state, | 219 | .get_cur_state = fan_get_cur_state, |
@@ -129,21 +225,125 @@ static const struct thermal_cooling_device_ops fan_cooling_ops = { | |||
129 | * -------------------------------------------------------------------------- | 225 | * -------------------------------------------------------------------------- |
130 | */ | 226 | */ |
131 | 227 | ||
132 | static int acpi_fan_add(struct acpi_device *device) | 228 | static bool acpi_fan_is_acpi4(struct acpi_device *device) |
133 | { | 229 | { |
134 | int result = 0; | 230 | return acpi_has_method(device->handle, "_FIF") && |
135 | struct thermal_cooling_device *cdev; | 231 | acpi_has_method(device->handle, "_FPS") && |
232 | acpi_has_method(device->handle, "_FSL") && | ||
233 | acpi_has_method(device->handle, "_FST"); | ||
234 | } | ||
136 | 235 | ||
137 | if (!device) | 236 | static int acpi_fan_get_fif(struct acpi_device *device) |
138 | return -EINVAL; | 237 | { |
238 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
239 | struct acpi_fan *fan = acpi_driver_data(device); | ||
240 | struct acpi_buffer format = { sizeof("NNNN"), "NNNN" }; | ||
241 | struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif }; | ||
242 | union acpi_object *obj; | ||
243 | acpi_status status; | ||
244 | |||
245 | status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer); | ||
246 | if (ACPI_FAILURE(status)) | ||
247 | return status; | ||
248 | |||
249 | obj = buffer.pointer; | ||
250 | if (!obj || obj->type != ACPI_TYPE_PACKAGE) { | ||
251 | dev_err(&device->dev, "Invalid _FIF data\n"); | ||
252 | status = -EINVAL; | ||
253 | goto err; | ||
254 | } | ||
139 | 255 | ||
140 | strcpy(acpi_device_name(device), "Fan"); | 256 | status = acpi_extract_package(obj, &format, &fif); |
141 | strcpy(acpi_device_class(device), ACPI_FAN_CLASS); | 257 | if (ACPI_FAILURE(status)) { |
258 | dev_err(&device->dev, "Invalid _FIF element\n"); | ||
259 | status = -EINVAL; | ||
260 | } | ||
142 | 261 | ||
143 | result = acpi_bus_update_power(device->handle, NULL); | 262 | err: |
144 | if (result) { | 263 | kfree(obj); |
145 | dev_err(&device->dev, "Setting initial power state\n"); | 264 | return status; |
146 | goto end; | 265 | } |
266 | |||
267 | static int acpi_fan_speed_cmp(const void *a, const void *b) | ||
268 | { | ||
269 | const struct acpi_fan_fps *fps1 = a; | ||
270 | const struct acpi_fan_fps *fps2 = b; | ||
271 | return fps1->speed - fps2->speed; | ||
272 | } | ||
273 | |||
274 | static int acpi_fan_get_fps(struct acpi_device *device) | ||
275 | { | ||
276 | struct acpi_fan *fan = acpi_driver_data(device); | ||
277 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
278 | union acpi_object *obj; | ||
279 | acpi_status status; | ||
280 | int i; | ||
281 | |||
282 | status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer); | ||
283 | if (ACPI_FAILURE(status)) | ||
284 | return status; | ||
285 | |||
286 | obj = buffer.pointer; | ||
287 | if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) { | ||
288 | dev_err(&device->dev, "Invalid _FPS data\n"); | ||
289 | status = -EINVAL; | ||
290 | goto err; | ||
291 | } | ||
292 | |||
293 | fan->fps_count = obj->package.count - 1; /* minus revision field */ | ||
294 | fan->fps = devm_kzalloc(&device->dev, | ||
295 | fan->fps_count * sizeof(struct acpi_fan_fps), | ||
296 | GFP_KERNEL); | ||
297 | if (!fan->fps) { | ||
298 | dev_err(&device->dev, "Not enough memory\n"); | ||
299 | status = -ENOMEM; | ||
300 | goto err; | ||
301 | } | ||
302 | for (i = 0; i < fan->fps_count; i++) { | ||
303 | struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" }; | ||
304 | struct acpi_buffer fps = { sizeof(fan->fps[i]), &fan->fps[i] }; | ||
305 | status = acpi_extract_package(&obj->package.elements[i + 1], | ||
306 | &format, &fps); | ||
307 | if (ACPI_FAILURE(status)) { | ||
308 | dev_err(&device->dev, "Invalid _FPS element\n"); | ||
309 | break; | ||
310 | } | ||
311 | } | ||
312 | |||
313 | /* sort the state array according to fan speed in increase order */ | ||
314 | sort(fan->fps, fan->fps_count, sizeof(*fan->fps), | ||
315 | acpi_fan_speed_cmp, NULL); | ||
316 | |||
317 | err: | ||
318 | kfree(obj); | ||
319 | return status; | ||
320 | } | ||
321 | |||
322 | static int acpi_fan_probe(struct platform_device *pdev) | ||
323 | { | ||
324 | int result = 0; | ||
325 | struct thermal_cooling_device *cdev; | ||
326 | struct acpi_fan *fan; | ||
327 | struct acpi_device *device = ACPI_COMPANION(&pdev->dev); | ||
328 | |||
329 | fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL); | ||
330 | if (!fan) { | ||
331 | dev_err(&device->dev, "No memory for fan\n"); | ||
332 | return -ENOMEM; | ||
333 | } | ||
334 | device->driver_data = fan; | ||
335 | platform_set_drvdata(pdev, fan); | ||
336 | |||
337 | if (acpi_fan_is_acpi4(device)) { | ||
338 | if (acpi_fan_get_fif(device) || acpi_fan_get_fps(device)) | ||
339 | goto end; | ||
340 | fan->acpi4 = true; | ||
341 | } else { | ||
342 | result = acpi_device_update_power(device, NULL); | ||
343 | if (result) { | ||
344 | dev_err(&device->dev, "Setting initial power state\n"); | ||
345 | goto end; | ||
346 | } | ||
147 | } | 347 | } |
148 | 348 | ||
149 | cdev = thermal_cooling_device_register("Fan", device, | 349 | cdev = thermal_cooling_device_register("Fan", device, |
@@ -153,44 +353,32 @@ static int acpi_fan_add(struct acpi_device *device) | |||
153 | goto end; | 353 | goto end; |
154 | } | 354 | } |
155 | 355 | ||
156 | dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id); | 356 | dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id); |
157 | 357 | ||
158 | device->driver_data = cdev; | 358 | fan->cdev = cdev; |
159 | result = sysfs_create_link(&device->dev.kobj, | 359 | result = sysfs_create_link(&pdev->dev.kobj, |
160 | &cdev->device.kobj, | 360 | &cdev->device.kobj, |
161 | "thermal_cooling"); | 361 | "thermal_cooling"); |
162 | if (result) | 362 | if (result) |
163 | dev_err(&device->dev, "Failed to create sysfs link " | 363 | dev_err(&pdev->dev, "Failed to create sysfs link 'thermal_cooling'\n"); |
164 | "'thermal_cooling'\n"); | ||
165 | 364 | ||
166 | result = sysfs_create_link(&cdev->device.kobj, | 365 | result = sysfs_create_link(&cdev->device.kobj, |
167 | &device->dev.kobj, | 366 | &pdev->dev.kobj, |
168 | "device"); | 367 | "device"); |
169 | if (result) | 368 | if (result) |
170 | dev_err(&device->dev, "Failed to create sysfs link 'device'\n"); | 369 | dev_err(&pdev->dev, "Failed to create sysfs link 'device'\n"); |
171 | |||
172 | dev_info(&device->dev, "ACPI: %s [%s] (%s)\n", | ||
173 | acpi_device_name(device), acpi_device_bid(device), | ||
174 | !device->power.state ? "on" : "off"); | ||
175 | 370 | ||
176 | end: | 371 | end: |
177 | return result; | 372 | return result; |
178 | } | 373 | } |
179 | 374 | ||
180 | static int acpi_fan_remove(struct acpi_device *device) | 375 | static int acpi_fan_remove(struct platform_device *pdev) |
181 | { | 376 | { |
182 | struct thermal_cooling_device *cdev; | 377 | struct acpi_fan *fan = platform_get_drvdata(pdev); |
183 | |||
184 | if (!device) | ||
185 | return -EINVAL; | ||
186 | |||
187 | cdev = acpi_driver_data(device); | ||
188 | if (!cdev) | ||
189 | return -EINVAL; | ||
190 | 378 | ||
191 | sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); | 379 | sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling"); |
192 | sysfs_remove_link(&cdev->device.kobj, "device"); | 380 | sysfs_remove_link(&fan->cdev->device.kobj, "device"); |
193 | thermal_cooling_device_unregister(cdev); | 381 | thermal_cooling_device_unregister(fan->cdev); |
194 | 382 | ||
195 | return 0; | 383 | return 0; |
196 | } | 384 | } |
@@ -198,10 +386,11 @@ static int acpi_fan_remove(struct acpi_device *device) | |||
198 | #ifdef CONFIG_PM_SLEEP | 386 | #ifdef CONFIG_PM_SLEEP |
199 | static int acpi_fan_suspend(struct device *dev) | 387 | static int acpi_fan_suspend(struct device *dev) |
200 | { | 388 | { |
201 | if (!dev) | 389 | struct acpi_fan *fan = dev_get_drvdata(dev); |
202 | return -EINVAL; | 390 | if (fan->acpi4) |
391 | return 0; | ||
203 | 392 | ||
204 | acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0); | 393 | acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0); |
205 | 394 | ||
206 | return AE_OK; | 395 | return AE_OK; |
207 | } | 396 | } |
@@ -209,11 +398,12 @@ static int acpi_fan_suspend(struct device *dev) | |||
209 | static int acpi_fan_resume(struct device *dev) | 398 | static int acpi_fan_resume(struct device *dev) |
210 | { | 399 | { |
211 | int result; | 400 | int result; |
401 | struct acpi_fan *fan = dev_get_drvdata(dev); | ||
212 | 402 | ||
213 | if (!dev) | 403 | if (fan->acpi4) |
214 | return -EINVAL; | 404 | return 0; |
215 | 405 | ||
216 | result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL); | 406 | result = acpi_device_update_power(ACPI_COMPANION(dev), NULL); |
217 | if (result) | 407 | if (result) |
218 | dev_err(dev, "Error updating fan power state\n"); | 408 | dev_err(dev, "Error updating fan power state\n"); |
219 | 409 | ||
@@ -221,4 +411,4 @@ static int acpi_fan_resume(struct device *dev) | |||
221 | } | 411 | } |
222 | #endif | 412 | #endif |
223 | 413 | ||
224 | module_acpi_driver(acpi_fan_driver); | 414 | module_platform_driver(acpi_fan_driver); |
diff --git a/drivers/acpi/int340x_thermal.c b/drivers/acpi/int340x_thermal.c new file mode 100644 index 000000000000..a27d31d1ba24 --- /dev/null +++ b/drivers/acpi/int340x_thermal.c | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * ACPI support for int340x thermal drivers | ||
3 | * | ||
4 | * Copyright (C) 2014, Intel Corporation | ||
5 | * Authors: Zhang Rui <rui.zhang@intel.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/acpi.h> | ||
13 | #include <linux/module.h> | ||
14 | |||
15 | #include "internal.h" | ||
16 | |||
17 | #define DO_ENUMERATION 0x01 | ||
18 | static const struct acpi_device_id int340x_thermal_device_ids[] = { | ||
19 | {"INT3400", DO_ENUMERATION }, | ||
20 | {"INT3401"}, | ||
21 | {"INT3402"}, | ||
22 | {"INT3403"}, | ||
23 | {"INT3404"}, | ||
24 | {"INT3406"}, | ||
25 | {"INT3407"}, | ||
26 | {"INT3408"}, | ||
27 | {"INT3409"}, | ||
28 | {"INT340A"}, | ||
29 | {"INT340B"}, | ||
30 | {""}, | ||
31 | }; | ||
32 | |||
33 | static int int340x_thermal_handler_attach(struct acpi_device *adev, | ||
34 | const struct acpi_device_id *id) | ||
35 | { | ||
36 | #if defined(CONFIG_INT340X_THERMAL) || defined(CONFIG_INT340X_THERMAL_MODULE) | ||
37 | if (id->driver_data == DO_ENUMERATION) | ||
38 | acpi_create_platform_device(adev); | ||
39 | #endif | ||
40 | return 1; | ||
41 | } | ||
42 | |||
43 | static struct acpi_scan_handler int340x_thermal_handler = { | ||
44 | .ids = int340x_thermal_device_ids, | ||
45 | .attach = int340x_thermal_handler_attach, | ||
46 | }; | ||
47 | |||
48 | void __init acpi_int340x_thermal_init(void) | ||
49 | { | ||
50 | acpi_scan_add_handler(&int340x_thermal_handler); | ||
51 | } | ||
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 4c5cf77e7576..447f6d679b29 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
@@ -31,6 +31,7 @@ void acpi_pci_link_init(void); | |||
31 | void acpi_processor_init(void); | 31 | void acpi_processor_init(void); |
32 | void acpi_platform_init(void); | 32 | void acpi_platform_init(void); |
33 | void acpi_pnp_init(void); | 33 | void acpi_pnp_init(void); |
34 | void acpi_int340x_thermal_init(void); | ||
34 | int acpi_sysfs_init(void); | 35 | int acpi_sysfs_init(void); |
35 | void acpi_container_init(void); | 36 | void acpi_container_init(void); |
36 | void acpi_memory_hotplug_init(void); | 37 | void acpi_memory_hotplug_init(void); |
@@ -103,8 +104,6 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state); | |||
103 | int acpi_power_on_resources(struct acpi_device *device, int state); | 104 | int acpi_power_on_resources(struct acpi_device *device, int state); |
104 | int acpi_power_transition(struct acpi_device *device, int state); | 105 | int acpi_power_transition(struct acpi_device *device, int state); |
105 | 106 | ||
106 | int acpi_device_update_power(struct acpi_device *device, int *state_p); | ||
107 | |||
108 | int acpi_wakeup_device_init(void); | 107 | int acpi_wakeup_device_init(void); |
109 | 108 | ||
110 | #ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC | 109 | #ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC |
@@ -168,13 +167,6 @@ static inline void suspend_nvs_restore(void) {} | |||
168 | #endif | 167 | #endif |
169 | 168 | ||
170 | /*-------------------------------------------------------------------------- | 169 | /*-------------------------------------------------------------------------- |
171 | Platform bus support | ||
172 | -------------------------------------------------------------------------- */ | ||
173 | struct platform_device; | ||
174 | |||
175 | struct platform_device *acpi_create_platform_device(struct acpi_device *adev); | ||
176 | |||
177 | /*-------------------------------------------------------------------------- | ||
178 | Video | 170 | Video |
179 | -------------------------------------------------------------------------- */ | 171 | -------------------------------------------------------------------------- */ |
180 | #if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) | 172 | #if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) |
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ae44d8654c82..d670158a26c5 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -1470,7 +1470,7 @@ static void acpi_wakeup_gpe_init(struct acpi_device *device) | |||
1470 | if (ACPI_FAILURE(status)) | 1470 | if (ACPI_FAILURE(status)) |
1471 | return; | 1471 | return; |
1472 | 1472 | ||
1473 | wakeup->flags.run_wake = !!(event_status & ACPI_EVENT_FLAG_HANDLE); | 1473 | wakeup->flags.run_wake = !!(event_status & ACPI_EVENT_FLAG_HAS_HANDLER); |
1474 | } | 1474 | } |
1475 | 1475 | ||
1476 | static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) | 1476 | static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) |
@@ -2315,6 +2315,7 @@ int __init acpi_scan_init(void) | |||
2315 | acpi_container_init(); | 2315 | acpi_container_init(); |
2316 | acpi_memory_hotplug_init(); | 2316 | acpi_memory_hotplug_init(); |
2317 | acpi_pnp_init(); | 2317 | acpi_pnp_init(); |
2318 | acpi_int340x_thermal_init(); | ||
2318 | 2319 | ||
2319 | mutex_lock(&acpi_scan_lock); | 2320 | mutex_lock(&acpi_scan_lock); |
2320 | /* | 2321 | /* |
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 38cb9782d4b8..13e577c80201 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c | |||
@@ -537,7 +537,7 @@ static ssize_t counter_show(struct kobject *kobj, | |||
537 | if (result) | 537 | if (result) |
538 | goto end; | 538 | goto end; |
539 | 539 | ||
540 | if (!(status & ACPI_EVENT_FLAG_HANDLE)) | 540 | if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) |
541 | size += sprintf(buf + size, " invalid"); | 541 | size += sprintf(buf + size, " invalid"); |
542 | else if (status & ACPI_EVENT_FLAG_ENABLED) | 542 | else if (status & ACPI_EVENT_FLAG_ENABLED) |
543 | size += sprintf(buf + size, " enabled"); | 543 | size += sprintf(buf + size, " enabled"); |
@@ -581,7 +581,7 @@ static ssize_t counter_set(struct kobject *kobj, | |||
581 | if (result) | 581 | if (result) |
582 | goto end; | 582 | goto end; |
583 | 583 | ||
584 | if (!(status & ACPI_EVENT_FLAG_HANDLE)) { | 584 | if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) { |
585 | printk(KERN_WARNING PREFIX | 585 | printk(KERN_WARNING PREFIX |
586 | "Can not change Invalid GPE/Fixed Event status\n"); | 586 | "Can not change Invalid GPE/Fixed Event status\n"); |
587 | return -EINVAL; | 587 | return -EINVAL; |
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 112817e963e0..d24fa1964eb8 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c | |||
@@ -528,7 +528,6 @@ static void acpi_thermal_check(void *data) | |||
528 | } | 528 | } |
529 | 529 | ||
530 | /* sys I/F for generic thermal sysfs support */ | 530 | /* sys I/F for generic thermal sysfs support */ |
531 | #define KELVIN_TO_MILLICELSIUS(t, off) (((t) - (off)) * 100) | ||
532 | 531 | ||
533 | static int thermal_get_temp(struct thermal_zone_device *thermal, | 532 | static int thermal_get_temp(struct thermal_zone_device *thermal, |
534 | unsigned long *temp) | 533 | unsigned long *temp) |
@@ -543,7 +542,8 @@ static int thermal_get_temp(struct thermal_zone_device *thermal, | |||
543 | if (result) | 542 | if (result) |
544 | return result; | 543 | return result; |
545 | 544 | ||
546 | *temp = KELVIN_TO_MILLICELSIUS(tz->temperature, tz->kelvin_offset); | 545 | *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(tz->temperature, |
546 | tz->kelvin_offset); | ||
547 | return 0; | 547 | return 0; |
548 | } | 548 | } |
549 | 549 | ||
@@ -647,7 +647,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, | |||
647 | 647 | ||
648 | if (tz->trips.critical.flags.valid) { | 648 | if (tz->trips.critical.flags.valid) { |
649 | if (!trip) { | 649 | if (!trip) { |
650 | *temp = KELVIN_TO_MILLICELSIUS( | 650 | *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( |
651 | tz->trips.critical.temperature, | 651 | tz->trips.critical.temperature, |
652 | tz->kelvin_offset); | 652 | tz->kelvin_offset); |
653 | return 0; | 653 | return 0; |
@@ -657,7 +657,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, | |||
657 | 657 | ||
658 | if (tz->trips.hot.flags.valid) { | 658 | if (tz->trips.hot.flags.valid) { |
659 | if (!trip) { | 659 | if (!trip) { |
660 | *temp = KELVIN_TO_MILLICELSIUS( | 660 | *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( |
661 | tz->trips.hot.temperature, | 661 | tz->trips.hot.temperature, |
662 | tz->kelvin_offset); | 662 | tz->kelvin_offset); |
663 | return 0; | 663 | return 0; |
@@ -667,7 +667,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, | |||
667 | 667 | ||
668 | if (tz->trips.passive.flags.valid) { | 668 | if (tz->trips.passive.flags.valid) { |
669 | if (!trip) { | 669 | if (!trip) { |
670 | *temp = KELVIN_TO_MILLICELSIUS( | 670 | *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( |
671 | tz->trips.passive.temperature, | 671 | tz->trips.passive.temperature, |
672 | tz->kelvin_offset); | 672 | tz->kelvin_offset); |
673 | return 0; | 673 | return 0; |
@@ -678,7 +678,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, | |||
678 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && | 678 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && |
679 | tz->trips.active[i].flags.valid; i++) { | 679 | tz->trips.active[i].flags.valid; i++) { |
680 | if (!trip) { | 680 | if (!trip) { |
681 | *temp = KELVIN_TO_MILLICELSIUS( | 681 | *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( |
682 | tz->trips.active[i].temperature, | 682 | tz->trips.active[i].temperature, |
683 | tz->kelvin_offset); | 683 | tz->kelvin_offset); |
684 | return 0; | 684 | return 0; |
@@ -694,7 +694,7 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal, | |||
694 | struct acpi_thermal *tz = thermal->devdata; | 694 | struct acpi_thermal *tz = thermal->devdata; |
695 | 695 | ||
696 | if (tz->trips.critical.flags.valid) { | 696 | if (tz->trips.critical.flags.valid) { |
697 | *temperature = KELVIN_TO_MILLICELSIUS( | 697 | *temperature = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( |
698 | tz->trips.critical.temperature, | 698 | tz->trips.critical.temperature, |
699 | tz->kelvin_offset); | 699 | tz->kelvin_offset); |
700 | return 0; | 700 | return 0; |
@@ -714,8 +714,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, | |||
714 | 714 | ||
715 | if (type == THERMAL_TRIP_ACTIVE) { | 715 | if (type == THERMAL_TRIP_ACTIVE) { |
716 | unsigned long trip_temp; | 716 | unsigned long trip_temp; |
717 | unsigned long temp = KELVIN_TO_MILLICELSIUS(tz->temperature, | 717 | unsigned long temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET( |
718 | tz->kelvin_offset); | 718 | tz->temperature, tz->kelvin_offset); |
719 | if (thermal_get_trip_temp(thermal, trip, &trip_temp)) | 719 | if (thermal_get_trip_temp(thermal, trip, &trip_temp)) |
720 | return -EINVAL; | 720 | return -EINVAL; |
721 | 721 | ||
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 834f35c4bf8d..371ac12d25b1 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c | |||
@@ -149,6 +149,21 @@ acpi_extract_package(union acpi_object *package, | |||
149 | break; | 149 | break; |
150 | } | 150 | } |
151 | break; | 151 | break; |
152 | case ACPI_TYPE_LOCAL_REFERENCE: | ||
153 | switch (format_string[i]) { | ||
154 | case 'R': | ||
155 | size_required += sizeof(void *); | ||
156 | tail_offset += sizeof(void *); | ||
157 | break; | ||
158 | default: | ||
159 | printk(KERN_WARNING PREFIX "Invalid package element" | ||
160 | " [%d] got reference," | ||
161 | " expecting [%c]\n", | ||
162 | i, format_string[i]); | ||
163 | return AE_BAD_DATA; | ||
164 | break; | ||
165 | } | ||
166 | break; | ||
152 | 167 | ||
153 | case ACPI_TYPE_PACKAGE: | 168 | case ACPI_TYPE_PACKAGE: |
154 | default: | 169 | default: |
@@ -247,7 +262,18 @@ acpi_extract_package(union acpi_object *package, | |||
247 | break; | 262 | break; |
248 | } | 263 | } |
249 | break; | 264 | break; |
250 | 265 | case ACPI_TYPE_LOCAL_REFERENCE: | |
266 | switch (format_string[i]) { | ||
267 | case 'R': | ||
268 | *(void **)head = | ||
269 | (void *)element->reference.handle; | ||
270 | head += sizeof(void *); | ||
271 | break; | ||
272 | default: | ||
273 | /* Should never get here */ | ||
274 | break; | ||
275 | } | ||
276 | break; | ||
251 | case ACPI_TYPE_PACKAGE: | 277 | case ACPI_TYPE_PACKAGE: |
252 | /* TBD: handle nested packages... */ | 278 | /* TBD: handle nested packages... */ |
253 | default: | 279 | default: |
diff --git a/drivers/char/random.c b/drivers/char/random.c index 82759cef9043..04645c09fe5e 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c | |||
@@ -1106,7 +1106,7 @@ static void extract_buf(struct entropy_store *r, __u8 *out) | |||
1106 | __mix_pool_bytes(r, hash.w, sizeof(hash.w)); | 1106 | __mix_pool_bytes(r, hash.w, sizeof(hash.w)); |
1107 | spin_unlock_irqrestore(&r->lock, flags); | 1107 | spin_unlock_irqrestore(&r->lock, flags); |
1108 | 1108 | ||
1109 | memset(workspace, 0, sizeof(workspace)); | 1109 | memzero_explicit(workspace, sizeof(workspace)); |
1110 | 1110 | ||
1111 | /* | 1111 | /* |
1112 | * In case the hash function has some recognizable output | 1112 | * In case the hash function has some recognizable output |
@@ -1118,7 +1118,7 @@ static void extract_buf(struct entropy_store *r, __u8 *out) | |||
1118 | hash.w[2] ^= rol32(hash.w[2], 16); | 1118 | hash.w[2] ^= rol32(hash.w[2], 16); |
1119 | 1119 | ||
1120 | memcpy(out, &hash, EXTRACT_SIZE); | 1120 | memcpy(out, &hash, EXTRACT_SIZE); |
1121 | memset(&hash, 0, sizeof(hash)); | 1121 | memzero_explicit(&hash, sizeof(hash)); |
1122 | } | 1122 | } |
1123 | 1123 | ||
1124 | /* | 1124 | /* |
@@ -1175,7 +1175,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, | |||
1175 | } | 1175 | } |
1176 | 1176 | ||
1177 | /* Wipe data just returned from memory */ | 1177 | /* Wipe data just returned from memory */ |
1178 | memset(tmp, 0, sizeof(tmp)); | 1178 | memzero_explicit(tmp, sizeof(tmp)); |
1179 | 1179 | ||
1180 | return ret; | 1180 | return ret; |
1181 | } | 1181 | } |
@@ -1218,7 +1218,7 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, | |||
1218 | } | 1218 | } |
1219 | 1219 | ||
1220 | /* Wipe data just returned from memory */ | 1220 | /* Wipe data just returned from memory */ |
1221 | memset(tmp, 0, sizeof(tmp)); | 1221 | memzero_explicit(tmp, sizeof(tmp)); |
1222 | 1222 | ||
1223 | return ret; | 1223 | return ret; |
1224 | } | 1224 | } |
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 6bbb8b913446..92c162af5045 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/cpu.h> | 18 | #include <linux/cpu.h> |
19 | #include <linux/cpu_cooling.h> | 19 | #include <linux/cpu_cooling.h> |
20 | #include <linux/cpufreq.h> | 20 | #include <linux/cpufreq.h> |
21 | #include <linux/cpufreq-dt.h> | ||
21 | #include <linux/cpumask.h> | 22 | #include <linux/cpumask.h> |
22 | #include <linux/err.h> | 23 | #include <linux/err.h> |
23 | #include <linux/module.h> | 24 | #include <linux/module.h> |
@@ -146,8 +147,8 @@ try_again: | |||
146 | goto try_again; | 147 | goto try_again; |
147 | } | 148 | } |
148 | 149 | ||
149 | dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n", | 150 | dev_dbg(cpu_dev, "no regulator for cpu%d: %ld\n", |
150 | cpu, PTR_ERR(cpu_reg)); | 151 | cpu, PTR_ERR(cpu_reg)); |
151 | } | 152 | } |
152 | 153 | ||
153 | cpu_clk = clk_get(cpu_dev, NULL); | 154 | cpu_clk = clk_get(cpu_dev, NULL); |
@@ -178,6 +179,7 @@ try_again: | |||
178 | 179 | ||
179 | static int cpufreq_init(struct cpufreq_policy *policy) | 180 | static int cpufreq_init(struct cpufreq_policy *policy) |
180 | { | 181 | { |
182 | struct cpufreq_dt_platform_data *pd; | ||
181 | struct cpufreq_frequency_table *freq_table; | 183 | struct cpufreq_frequency_table *freq_table; |
182 | struct thermal_cooling_device *cdev; | 184 | struct thermal_cooling_device *cdev; |
183 | struct device_node *np; | 185 | struct device_node *np; |
@@ -265,9 +267,18 @@ static int cpufreq_init(struct cpufreq_policy *policy) | |||
265 | policy->driver_data = priv; | 267 | policy->driver_data = priv; |
266 | 268 | ||
267 | policy->clk = cpu_clk; | 269 | policy->clk = cpu_clk; |
268 | ret = cpufreq_generic_init(policy, freq_table, transition_latency); | 270 | ret = cpufreq_table_validate_and_show(policy, freq_table); |
269 | if (ret) | 271 | if (ret) { |
272 | dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__, | ||
273 | ret); | ||
270 | goto out_cooling_unregister; | 274 | goto out_cooling_unregister; |
275 | } | ||
276 | |||
277 | policy->cpuinfo.transition_latency = transition_latency; | ||
278 | |||
279 | pd = cpufreq_get_driver_data(); | ||
280 | if (pd && !pd->independent_clocks) | ||
281 | cpumask_setall(policy->cpus); | ||
271 | 282 | ||
272 | of_node_put(np); | 283 | of_node_put(np); |
273 | 284 | ||
@@ -335,6 +346,8 @@ static int dt_cpufreq_probe(struct platform_device *pdev) | |||
335 | if (!IS_ERR(cpu_reg)) | 346 | if (!IS_ERR(cpu_reg)) |
336 | regulator_put(cpu_reg); | 347 | regulator_put(cpu_reg); |
337 | 348 | ||
349 | dt_cpufreq_driver.driver_data = dev_get_platdata(&pdev->dev); | ||
350 | |||
338 | ret = cpufreq_register_driver(&dt_cpufreq_driver); | 351 | ret = cpufreq_register_driver(&dt_cpufreq_driver); |
339 | if (ret) | 352 | if (ret) |
340 | dev_err(cpu_dev, "failed register driver: %d\n", ret); | 353 | dev_err(cpu_dev, "failed register driver: %d\n", ret); |
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 24bf76fba141..644b54e1e7d1 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c | |||
@@ -512,7 +512,18 @@ show_one(cpuinfo_max_freq, cpuinfo.max_freq); | |||
512 | show_one(cpuinfo_transition_latency, cpuinfo.transition_latency); | 512 | show_one(cpuinfo_transition_latency, cpuinfo.transition_latency); |
513 | show_one(scaling_min_freq, min); | 513 | show_one(scaling_min_freq, min); |
514 | show_one(scaling_max_freq, max); | 514 | show_one(scaling_max_freq, max); |
515 | show_one(scaling_cur_freq, cur); | 515 | |
516 | static ssize_t show_scaling_cur_freq( | ||
517 | struct cpufreq_policy *policy, char *buf) | ||
518 | { | ||
519 | ssize_t ret; | ||
520 | |||
521 | if (cpufreq_driver && cpufreq_driver->setpolicy && cpufreq_driver->get) | ||
522 | ret = sprintf(buf, "%u\n", cpufreq_driver->get(policy->cpu)); | ||
523 | else | ||
524 | ret = sprintf(buf, "%u\n", policy->cur); | ||
525 | return ret; | ||
526 | } | ||
516 | 527 | ||
517 | static int cpufreq_set_policy(struct cpufreq_policy *policy, | 528 | static int cpufreq_set_policy(struct cpufreq_policy *policy, |
518 | struct cpufreq_policy *new_policy); | 529 | struct cpufreq_policy *new_policy); |
@@ -906,11 +917,11 @@ static int cpufreq_add_dev_interface(struct cpufreq_policy *policy, | |||
906 | if (ret) | 917 | if (ret) |
907 | goto err_out_kobj_put; | 918 | goto err_out_kobj_put; |
908 | } | 919 | } |
909 | if (has_target()) { | 920 | |
910 | ret = sysfs_create_file(&policy->kobj, &scaling_cur_freq.attr); | 921 | ret = sysfs_create_file(&policy->kobj, &scaling_cur_freq.attr); |
911 | if (ret) | 922 | if (ret) |
912 | goto err_out_kobj_put; | 923 | goto err_out_kobj_put; |
913 | } | 924 | |
914 | if (cpufreq_driver->bios_limit) { | 925 | if (cpufreq_driver->bios_limit) { |
915 | ret = sysfs_create_file(&policy->kobj, &bios_limit.attr); | 926 | ret = sysfs_create_file(&policy->kobj, &bios_limit.attr); |
916 | if (ret) | 927 | if (ret) |
@@ -1731,6 +1742,21 @@ const char *cpufreq_get_current_driver(void) | |||
1731 | } | 1742 | } |
1732 | EXPORT_SYMBOL_GPL(cpufreq_get_current_driver); | 1743 | EXPORT_SYMBOL_GPL(cpufreq_get_current_driver); |
1733 | 1744 | ||
1745 | /** | ||
1746 | * cpufreq_get_driver_data - return current driver data | ||
1747 | * | ||
1748 | * Return the private data of the currently loaded cpufreq | ||
1749 | * driver, or NULL if no cpufreq driver is loaded. | ||
1750 | */ | ||
1751 | void *cpufreq_get_driver_data(void) | ||
1752 | { | ||
1753 | if (cpufreq_driver) | ||
1754 | return cpufreq_driver->driver_data; | ||
1755 | |||
1756 | return NULL; | ||
1757 | } | ||
1758 | EXPORT_SYMBOL_GPL(cpufreq_get_driver_data); | ||
1759 | |||
1734 | /********************************************************************* | 1760 | /********************************************************************* |
1735 | * NOTIFIER LISTS INTERFACE * | 1761 | * NOTIFIER LISTS INTERFACE * |
1736 | *********************************************************************/ | 1762 | *********************************************************************/ |
diff --git a/drivers/cpufreq/highbank-cpufreq.c b/drivers/cpufreq/highbank-cpufreq.c index ec399ad2f059..1608f7105c9f 100644 --- a/drivers/cpufreq/highbank-cpufreq.c +++ b/drivers/cpufreq/highbank-cpufreq.c | |||
@@ -19,7 +19,7 @@ | |||
19 | #include <linux/cpu.h> | 19 | #include <linux/cpu.h> |
20 | #include <linux/err.h> | 20 | #include <linux/err.h> |
21 | #include <linux/of.h> | 21 | #include <linux/of.h> |
22 | #include <linux/mailbox.h> | 22 | #include <linux/pl320-ipc.h> |
23 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
24 | 24 | ||
25 | #define HB_CPUFREQ_CHANGE_NOTE 0x80000001 | 25 | #define HB_CPUFREQ_CHANGE_NOTE 0x80000001 |
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 0668b389c516..27bb6d3877ed 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c | |||
@@ -52,6 +52,17 @@ static inline int32_t div_fp(int32_t x, int32_t y) | |||
52 | return div_s64((int64_t)x << FRAC_BITS, y); | 52 | return div_s64((int64_t)x << FRAC_BITS, y); |
53 | } | 53 | } |
54 | 54 | ||
55 | static inline int ceiling_fp(int32_t x) | ||
56 | { | ||
57 | int mask, ret; | ||
58 | |||
59 | ret = fp_toint(x); | ||
60 | mask = (1 << FRAC_BITS) - 1; | ||
61 | if (x & mask) | ||
62 | ret += 1; | ||
63 | return ret; | ||
64 | } | ||
65 | |||
55 | struct sample { | 66 | struct sample { |
56 | int32_t core_pct_busy; | 67 | int32_t core_pct_busy; |
57 | u64 aperf; | 68 | u64 aperf; |
@@ -64,6 +75,7 @@ struct pstate_data { | |||
64 | int current_pstate; | 75 | int current_pstate; |
65 | int min_pstate; | 76 | int min_pstate; |
66 | int max_pstate; | 77 | int max_pstate; |
78 | int scaling; | ||
67 | int turbo_pstate; | 79 | int turbo_pstate; |
68 | }; | 80 | }; |
69 | 81 | ||
@@ -113,6 +125,7 @@ struct pstate_funcs { | |||
113 | int (*get_max)(void); | 125 | int (*get_max)(void); |
114 | int (*get_min)(void); | 126 | int (*get_min)(void); |
115 | int (*get_turbo)(void); | 127 | int (*get_turbo)(void); |
128 | int (*get_scaling)(void); | ||
116 | void (*set)(struct cpudata*, int pstate); | 129 | void (*set)(struct cpudata*, int pstate); |
117 | void (*get_vid)(struct cpudata *); | 130 | void (*get_vid)(struct cpudata *); |
118 | }; | 131 | }; |
@@ -138,6 +151,7 @@ struct perf_limits { | |||
138 | 151 | ||
139 | static struct perf_limits limits = { | 152 | static struct perf_limits limits = { |
140 | .no_turbo = 0, | 153 | .no_turbo = 0, |
154 | .turbo_disabled = 0, | ||
141 | .max_perf_pct = 100, | 155 | .max_perf_pct = 100, |
142 | .max_perf = int_tofp(1), | 156 | .max_perf = int_tofp(1), |
143 | .min_perf_pct = 0, | 157 | .min_perf_pct = 0, |
@@ -218,6 +232,18 @@ static inline void intel_pstate_reset_all_pid(void) | |||
218 | } | 232 | } |
219 | } | 233 | } |
220 | 234 | ||
235 | static inline void update_turbo_state(void) | ||
236 | { | ||
237 | u64 misc_en; | ||
238 | struct cpudata *cpu; | ||
239 | |||
240 | cpu = all_cpu_data[0]; | ||
241 | rdmsrl(MSR_IA32_MISC_ENABLE, misc_en); | ||
242 | limits.turbo_disabled = | ||
243 | (misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE || | ||
244 | cpu->pstate.max_pstate == cpu->pstate.turbo_pstate); | ||
245 | } | ||
246 | |||
221 | /************************** debugfs begin ************************/ | 247 | /************************** debugfs begin ************************/ |
222 | static int pid_param_set(void *data, u64 val) | 248 | static int pid_param_set(void *data, u64 val) |
223 | { | 249 | { |
@@ -274,6 +300,20 @@ static void __init intel_pstate_debug_expose_params(void) | |||
274 | return sprintf(buf, "%u\n", limits.object); \ | 300 | return sprintf(buf, "%u\n", limits.object); \ |
275 | } | 301 | } |
276 | 302 | ||
303 | static ssize_t show_no_turbo(struct kobject *kobj, | ||
304 | struct attribute *attr, char *buf) | ||
305 | { | ||
306 | ssize_t ret; | ||
307 | |||
308 | update_turbo_state(); | ||
309 | if (limits.turbo_disabled) | ||
310 | ret = sprintf(buf, "%u\n", limits.turbo_disabled); | ||
311 | else | ||
312 | ret = sprintf(buf, "%u\n", limits.no_turbo); | ||
313 | |||
314 | return ret; | ||
315 | } | ||
316 | |||
277 | static ssize_t store_no_turbo(struct kobject *a, struct attribute *b, | 317 | static ssize_t store_no_turbo(struct kobject *a, struct attribute *b, |
278 | const char *buf, size_t count) | 318 | const char *buf, size_t count) |
279 | { | 319 | { |
@@ -283,11 +323,14 @@ static ssize_t store_no_turbo(struct kobject *a, struct attribute *b, | |||
283 | ret = sscanf(buf, "%u", &input); | 323 | ret = sscanf(buf, "%u", &input); |
284 | if (ret != 1) | 324 | if (ret != 1) |
285 | return -EINVAL; | 325 | return -EINVAL; |
286 | limits.no_turbo = clamp_t(int, input, 0 , 1); | 326 | |
327 | update_turbo_state(); | ||
287 | if (limits.turbo_disabled) { | 328 | if (limits.turbo_disabled) { |
288 | pr_warn("Turbo disabled by BIOS or unavailable on processor\n"); | 329 | pr_warn("Turbo disabled by BIOS or unavailable on processor\n"); |
289 | limits.no_turbo = limits.turbo_disabled; | 330 | return -EPERM; |
290 | } | 331 | } |
332 | limits.no_turbo = clamp_t(int, input, 0, 1); | ||
333 | |||
291 | return count; | 334 | return count; |
292 | } | 335 | } |
293 | 336 | ||
@@ -323,7 +366,6 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct attribute *b, | |||
323 | return count; | 366 | return count; |
324 | } | 367 | } |
325 | 368 | ||
326 | show_one(no_turbo, no_turbo); | ||
327 | show_one(max_perf_pct, max_perf_pct); | 369 | show_one(max_perf_pct, max_perf_pct); |
328 | show_one(min_perf_pct, min_perf_pct); | 370 | show_one(min_perf_pct, min_perf_pct); |
329 | 371 | ||
@@ -394,7 +436,7 @@ static void byt_set_pstate(struct cpudata *cpudata, int pstate) | |||
394 | cpudata->vid.ratio); | 436 | cpudata->vid.ratio); |
395 | 437 | ||
396 | vid_fp = clamp_t(int32_t, vid_fp, cpudata->vid.min, cpudata->vid.max); | 438 | vid_fp = clamp_t(int32_t, vid_fp, cpudata->vid.min, cpudata->vid.max); |
397 | vid = fp_toint(vid_fp); | 439 | vid = ceiling_fp(vid_fp); |
398 | 440 | ||
399 | if (pstate > cpudata->pstate.max_pstate) | 441 | if (pstate > cpudata->pstate.max_pstate) |
400 | vid = cpudata->vid.turbo; | 442 | vid = cpudata->vid.turbo; |
@@ -404,6 +446,22 @@ static void byt_set_pstate(struct cpudata *cpudata, int pstate) | |||
404 | wrmsrl(MSR_IA32_PERF_CTL, val); | 446 | wrmsrl(MSR_IA32_PERF_CTL, val); |
405 | } | 447 | } |
406 | 448 | ||
449 | #define BYT_BCLK_FREQS 5 | ||
450 | static int byt_freq_table[BYT_BCLK_FREQS] = { 833, 1000, 1333, 1167, 800}; | ||
451 | |||
452 | static int byt_get_scaling(void) | ||
453 | { | ||
454 | u64 value; | ||
455 | int i; | ||
456 | |||
457 | rdmsrl(MSR_FSB_FREQ, value); | ||
458 | i = value & 0x3; | ||
459 | |||
460 | BUG_ON(i > BYT_BCLK_FREQS); | ||
461 | |||
462 | return byt_freq_table[i] * 100; | ||
463 | } | ||
464 | |||
407 | static void byt_get_vid(struct cpudata *cpudata) | 465 | static void byt_get_vid(struct cpudata *cpudata) |
408 | { | 466 | { |
409 | u64 value; | 467 | u64 value; |
@@ -449,6 +507,11 @@ static int core_get_turbo_pstate(void) | |||
449 | return ret; | 507 | return ret; |
450 | } | 508 | } |
451 | 509 | ||
510 | static inline int core_get_scaling(void) | ||
511 | { | ||
512 | return 100000; | ||
513 | } | ||
514 | |||
452 | static void core_set_pstate(struct cpudata *cpudata, int pstate) | 515 | static void core_set_pstate(struct cpudata *cpudata, int pstate) |
453 | { | 516 | { |
454 | u64 val; | 517 | u64 val; |
@@ -473,6 +536,7 @@ static struct cpu_defaults core_params = { | |||
473 | .get_max = core_get_max_pstate, | 536 | .get_max = core_get_max_pstate, |
474 | .get_min = core_get_min_pstate, | 537 | .get_min = core_get_min_pstate, |
475 | .get_turbo = core_get_turbo_pstate, | 538 | .get_turbo = core_get_turbo_pstate, |
539 | .get_scaling = core_get_scaling, | ||
476 | .set = core_set_pstate, | 540 | .set = core_set_pstate, |
477 | }, | 541 | }, |
478 | }; | 542 | }; |
@@ -491,6 +555,7 @@ static struct cpu_defaults byt_params = { | |||
491 | .get_min = byt_get_min_pstate, | 555 | .get_min = byt_get_min_pstate, |
492 | .get_turbo = byt_get_turbo_pstate, | 556 | .get_turbo = byt_get_turbo_pstate, |
493 | .set = byt_set_pstate, | 557 | .set = byt_set_pstate, |
558 | .get_scaling = byt_get_scaling, | ||
494 | .get_vid = byt_get_vid, | 559 | .get_vid = byt_get_vid, |
495 | }, | 560 | }, |
496 | }; | 561 | }; |
@@ -501,7 +566,7 @@ static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max) | |||
501 | int max_perf_adj; | 566 | int max_perf_adj; |
502 | int min_perf; | 567 | int min_perf; |
503 | 568 | ||
504 | if (limits.no_turbo) | 569 | if (limits.no_turbo || limits.turbo_disabled) |
505 | max_perf = cpu->pstate.max_pstate; | 570 | max_perf = cpu->pstate.max_pstate; |
506 | 571 | ||
507 | max_perf_adj = fp_toint(mul_fp(int_tofp(max_perf), limits.max_perf)); | 572 | max_perf_adj = fp_toint(mul_fp(int_tofp(max_perf), limits.max_perf)); |
@@ -516,6 +581,8 @@ static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) | |||
516 | { | 581 | { |
517 | int max_perf, min_perf; | 582 | int max_perf, min_perf; |
518 | 583 | ||
584 | update_turbo_state(); | ||
585 | |||
519 | intel_pstate_get_min_max(cpu, &min_perf, &max_perf); | 586 | intel_pstate_get_min_max(cpu, &min_perf, &max_perf); |
520 | 587 | ||
521 | pstate = clamp_t(int, pstate, min_perf, max_perf); | 588 | pstate = clamp_t(int, pstate, min_perf, max_perf); |
@@ -523,7 +590,7 @@ static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) | |||
523 | if (pstate == cpu->pstate.current_pstate) | 590 | if (pstate == cpu->pstate.current_pstate) |
524 | return; | 591 | return; |
525 | 592 | ||
526 | trace_cpu_frequency(pstate * 100000, cpu->cpu); | 593 | trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); |
527 | 594 | ||
528 | cpu->pstate.current_pstate = pstate; | 595 | cpu->pstate.current_pstate = pstate; |
529 | 596 | ||
@@ -535,6 +602,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) | |||
535 | cpu->pstate.min_pstate = pstate_funcs.get_min(); | 602 | cpu->pstate.min_pstate = pstate_funcs.get_min(); |
536 | cpu->pstate.max_pstate = pstate_funcs.get_max(); | 603 | cpu->pstate.max_pstate = pstate_funcs.get_max(); |
537 | cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(); | 604 | cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(); |
605 | cpu->pstate.scaling = pstate_funcs.get_scaling(); | ||
538 | 606 | ||
539 | if (pstate_funcs.get_vid) | 607 | if (pstate_funcs.get_vid) |
540 | pstate_funcs.get_vid(cpu); | 608 | pstate_funcs.get_vid(cpu); |
@@ -550,7 +618,9 @@ static inline void intel_pstate_calc_busy(struct cpudata *cpu) | |||
550 | core_pct = div64_u64(core_pct, int_tofp(sample->mperf)); | 618 | core_pct = div64_u64(core_pct, int_tofp(sample->mperf)); |
551 | 619 | ||
552 | sample->freq = fp_toint( | 620 | sample->freq = fp_toint( |
553 | mul_fp(int_tofp(cpu->pstate.max_pstate * 1000), core_pct)); | 621 | mul_fp(int_tofp( |
622 | cpu->pstate.max_pstate * cpu->pstate.scaling / 100), | ||
623 | core_pct)); | ||
554 | 624 | ||
555 | sample->core_pct_busy = (int32_t)core_pct; | 625 | sample->core_pct_busy = (int32_t)core_pct; |
556 | } | 626 | } |
@@ -671,7 +741,9 @@ static int intel_pstate_init_cpu(unsigned int cpunum) | |||
671 | { | 741 | { |
672 | struct cpudata *cpu; | 742 | struct cpudata *cpu; |
673 | 743 | ||
674 | all_cpu_data[cpunum] = kzalloc(sizeof(struct cpudata), GFP_KERNEL); | 744 | if (!all_cpu_data[cpunum]) |
745 | all_cpu_data[cpunum] = kzalloc(sizeof(struct cpudata), | ||
746 | GFP_KERNEL); | ||
675 | if (!all_cpu_data[cpunum]) | 747 | if (!all_cpu_data[cpunum]) |
676 | return -ENOMEM; | 748 | return -ENOMEM; |
677 | 749 | ||
@@ -714,9 +786,10 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) | |||
714 | if (policy->policy == CPUFREQ_POLICY_PERFORMANCE) { | 786 | if (policy->policy == CPUFREQ_POLICY_PERFORMANCE) { |
715 | limits.min_perf_pct = 100; | 787 | limits.min_perf_pct = 100; |
716 | limits.min_perf = int_tofp(1); | 788 | limits.min_perf = int_tofp(1); |
789 | limits.max_policy_pct = 100; | ||
717 | limits.max_perf_pct = 100; | 790 | limits.max_perf_pct = 100; |
718 | limits.max_perf = int_tofp(1); | 791 | limits.max_perf = int_tofp(1); |
719 | limits.no_turbo = limits.turbo_disabled; | 792 | limits.no_turbo = 0; |
720 | return 0; | 793 | return 0; |
721 | } | 794 | } |
722 | limits.min_perf_pct = (policy->min * 100) / policy->cpuinfo.max_freq; | 795 | limits.min_perf_pct = (policy->min * 100) / policy->cpuinfo.max_freq; |
@@ -751,15 +824,12 @@ static void intel_pstate_stop_cpu(struct cpufreq_policy *policy) | |||
751 | 824 | ||
752 | del_timer_sync(&all_cpu_data[cpu_num]->timer); | 825 | del_timer_sync(&all_cpu_data[cpu_num]->timer); |
753 | intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); | 826 | intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); |
754 | kfree(all_cpu_data[cpu_num]); | ||
755 | all_cpu_data[cpu_num] = NULL; | ||
756 | } | 827 | } |
757 | 828 | ||
758 | static int intel_pstate_cpu_init(struct cpufreq_policy *policy) | 829 | static int intel_pstate_cpu_init(struct cpufreq_policy *policy) |
759 | { | 830 | { |
760 | struct cpudata *cpu; | 831 | struct cpudata *cpu; |
761 | int rc; | 832 | int rc; |
762 | u64 misc_en; | ||
763 | 833 | ||
764 | rc = intel_pstate_init_cpu(policy->cpu); | 834 | rc = intel_pstate_init_cpu(policy->cpu); |
765 | if (rc) | 835 | if (rc) |
@@ -767,23 +837,18 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy) | |||
767 | 837 | ||
768 | cpu = all_cpu_data[policy->cpu]; | 838 | cpu = all_cpu_data[policy->cpu]; |
769 | 839 | ||
770 | rdmsrl(MSR_IA32_MISC_ENABLE, misc_en); | ||
771 | if (misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE || | ||
772 | cpu->pstate.max_pstate == cpu->pstate.turbo_pstate) { | ||
773 | limits.turbo_disabled = 1; | ||
774 | limits.no_turbo = 1; | ||
775 | } | ||
776 | if (limits.min_perf_pct == 100 && limits.max_perf_pct == 100) | 840 | if (limits.min_perf_pct == 100 && limits.max_perf_pct == 100) |
777 | policy->policy = CPUFREQ_POLICY_PERFORMANCE; | 841 | policy->policy = CPUFREQ_POLICY_PERFORMANCE; |
778 | else | 842 | else |
779 | policy->policy = CPUFREQ_POLICY_POWERSAVE; | 843 | policy->policy = CPUFREQ_POLICY_POWERSAVE; |
780 | 844 | ||
781 | policy->min = cpu->pstate.min_pstate * 100000; | 845 | policy->min = cpu->pstate.min_pstate * cpu->pstate.scaling; |
782 | policy->max = cpu->pstate.turbo_pstate * 100000; | 846 | policy->max = cpu->pstate.turbo_pstate * cpu->pstate.scaling; |
783 | 847 | ||
784 | /* cpuinfo and default policy values */ | 848 | /* cpuinfo and default policy values */ |
785 | policy->cpuinfo.min_freq = cpu->pstate.min_pstate * 100000; | 849 | policy->cpuinfo.min_freq = cpu->pstate.min_pstate * cpu->pstate.scaling; |
786 | policy->cpuinfo.max_freq = cpu->pstate.turbo_pstate * 100000; | 850 | policy->cpuinfo.max_freq = |
851 | cpu->pstate.turbo_pstate * cpu->pstate.scaling; | ||
787 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; | 852 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; |
788 | cpumask_set_cpu(policy->cpu, policy->cpus); | 853 | cpumask_set_cpu(policy->cpu, policy->cpus); |
789 | 854 | ||
@@ -841,6 +906,7 @@ static void copy_cpu_funcs(struct pstate_funcs *funcs) | |||
841 | pstate_funcs.get_max = funcs->get_max; | 906 | pstate_funcs.get_max = funcs->get_max; |
842 | pstate_funcs.get_min = funcs->get_min; | 907 | pstate_funcs.get_min = funcs->get_min; |
843 | pstate_funcs.get_turbo = funcs->get_turbo; | 908 | pstate_funcs.get_turbo = funcs->get_turbo; |
909 | pstate_funcs.get_scaling = funcs->get_scaling; | ||
844 | pstate_funcs.set = funcs->set; | 910 | pstate_funcs.set = funcs->set; |
845 | pstate_funcs.get_vid = funcs->get_vid; | 911 | pstate_funcs.get_vid = funcs->get_vid; |
846 | } | 912 | } |
diff --git a/drivers/cpuidle/Kconfig.mips b/drivers/cpuidle/Kconfig.mips index 0e70ee28a5ca..4102be01d06a 100644 --- a/drivers/cpuidle/Kconfig.mips +++ b/drivers/cpuidle/Kconfig.mips | |||
@@ -3,7 +3,7 @@ | |||
3 | # | 3 | # |
4 | config MIPS_CPS_CPUIDLE | 4 | config MIPS_CPS_CPUIDLE |
5 | bool "CPU Idle driver for MIPS CPS platforms" | 5 | bool "CPU Idle driver for MIPS CPS platforms" |
6 | depends on CPU_IDLE | 6 | depends on CPU_IDLE && MIPS_CPS |
7 | depends on SYS_SUPPORTS_MIPS_CPS | 7 | depends on SYS_SUPPORTS_MIPS_CPS |
8 | select ARCH_NEEDS_CPU_IDLE_COUPLED if MIPS_MT | 8 | select ARCH_NEEDS_CPU_IDLE_COUPLED if MIPS_MT |
9 | select GENERIC_CLOCKEVENTS_BROADCAST if SMP | 9 | select GENERIC_CLOCKEVENTS_BROADCAST if SMP |
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index a64be578dab2..7d3a3497dd4c 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c | |||
@@ -163,7 +163,8 @@ static int powernv_add_idle_states(void) | |||
163 | int nr_idle_states = 1; /* Snooze */ | 163 | int nr_idle_states = 1; /* Snooze */ |
164 | int dt_idle_states; | 164 | int dt_idle_states; |
165 | const __be32 *idle_state_flags; | 165 | const __be32 *idle_state_flags; |
166 | u32 len_flags, flags; | 166 | const __be32 *idle_state_latency; |
167 | u32 len_flags, flags, latency_ns; | ||
167 | int i; | 168 | int i; |
168 | 169 | ||
169 | /* Currently we have snooze statically defined */ | 170 | /* Currently we have snooze statically defined */ |
@@ -180,18 +181,32 @@ static int powernv_add_idle_states(void) | |||
180 | return nr_idle_states; | 181 | return nr_idle_states; |
181 | } | 182 | } |
182 | 183 | ||
184 | idle_state_latency = of_get_property(power_mgt, | ||
185 | "ibm,cpu-idle-state-latencies-ns", NULL); | ||
186 | if (!idle_state_latency) { | ||
187 | pr_warn("DT-PowerMgmt: missing ibm,cpu-idle-state-latencies-ns\n"); | ||
188 | return nr_idle_states; | ||
189 | } | ||
190 | |||
183 | dt_idle_states = len_flags / sizeof(u32); | 191 | dt_idle_states = len_flags / sizeof(u32); |
184 | 192 | ||
185 | for (i = 0; i < dt_idle_states; i++) { | 193 | for (i = 0; i < dt_idle_states; i++) { |
186 | 194 | ||
187 | flags = be32_to_cpu(idle_state_flags[i]); | 195 | flags = be32_to_cpu(idle_state_flags[i]); |
196 | |||
197 | /* Cpuidle accepts exit_latency in us and we estimate | ||
198 | * target residency to be 10x exit_latency | ||
199 | */ | ||
200 | latency_ns = be32_to_cpu(idle_state_latency[i]); | ||
188 | if (flags & IDLE_USE_INST_NAP) { | 201 | if (flags & IDLE_USE_INST_NAP) { |
189 | /* Add NAP state */ | 202 | /* Add NAP state */ |
190 | strcpy(powernv_states[nr_idle_states].name, "Nap"); | 203 | strcpy(powernv_states[nr_idle_states].name, "Nap"); |
191 | strcpy(powernv_states[nr_idle_states].desc, "Nap"); | 204 | strcpy(powernv_states[nr_idle_states].desc, "Nap"); |
192 | powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIME_VALID; | 205 | powernv_states[nr_idle_states].flags = CPUIDLE_FLAG_TIME_VALID; |
193 | powernv_states[nr_idle_states].exit_latency = 10; | 206 | powernv_states[nr_idle_states].exit_latency = |
194 | powernv_states[nr_idle_states].target_residency = 100; | 207 | ((unsigned int)latency_ns) / 1000; |
208 | powernv_states[nr_idle_states].target_residency = | ||
209 | ((unsigned int)latency_ns / 100); | ||
195 | powernv_states[nr_idle_states].enter = &nap_loop; | 210 | powernv_states[nr_idle_states].enter = &nap_loop; |
196 | nr_idle_states++; | 211 | nr_idle_states++; |
197 | } | 212 | } |
@@ -202,8 +217,10 @@ static int powernv_add_idle_states(void) | |||
202 | strcpy(powernv_states[nr_idle_states].desc, "FastSleep"); | 217 | strcpy(powernv_states[nr_idle_states].desc, "FastSleep"); |
203 | powernv_states[nr_idle_states].flags = | 218 | powernv_states[nr_idle_states].flags = |
204 | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TIMER_STOP; | 219 | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TIMER_STOP; |
205 | powernv_states[nr_idle_states].exit_latency = 300; | 220 | powernv_states[nr_idle_states].exit_latency = |
206 | powernv_states[nr_idle_states].target_residency = 1000000; | 221 | ((unsigned int)latency_ns) / 1000; |
222 | powernv_states[nr_idle_states].target_residency = | ||
223 | ((unsigned int)latency_ns / 100); | ||
207 | powernv_states[nr_idle_states].enter = &fastsleep_loop; | 224 | powernv_states[nr_idle_states].enter = &fastsleep_loop; |
208 | nr_idle_states++; | 225 | nr_idle_states++; |
209 | } | 226 | } |
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 64ecbb501c50..8590099ac148 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c | |||
@@ -41,6 +41,28 @@ struct efi __read_mostly efi = { | |||
41 | }; | 41 | }; |
42 | EXPORT_SYMBOL(efi); | 42 | EXPORT_SYMBOL(efi); |
43 | 43 | ||
44 | static bool disable_runtime; | ||
45 | static int __init setup_noefi(char *arg) | ||
46 | { | ||
47 | disable_runtime = true; | ||
48 | return 0; | ||
49 | } | ||
50 | early_param("noefi", setup_noefi); | ||
51 | |||
52 | bool efi_runtime_disabled(void) | ||
53 | { | ||
54 | return disable_runtime; | ||
55 | } | ||
56 | |||
57 | static int __init parse_efi_cmdline(char *str) | ||
58 | { | ||
59 | if (parse_option_str(str, "noruntime")) | ||
60 | disable_runtime = true; | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | early_param("efi", parse_efi_cmdline); | ||
65 | |||
44 | static struct kobject *efi_kobj; | 66 | static struct kobject *efi_kobj; |
45 | static struct kobject *efivars_kobj; | 67 | static struct kobject *efivars_kobj; |
46 | 68 | ||
@@ -423,3 +445,60 @@ int __init efi_get_fdt_params(struct efi_fdt_params *params, int verbose) | |||
423 | return ret; | 445 | return ret; |
424 | } | 446 | } |
425 | #endif /* CONFIG_EFI_PARAMS_FROM_FDT */ | 447 | #endif /* CONFIG_EFI_PARAMS_FROM_FDT */ |
448 | |||
449 | static __initdata char memory_type_name[][20] = { | ||
450 | "Reserved", | ||
451 | "Loader Code", | ||
452 | "Loader Data", | ||
453 | "Boot Code", | ||
454 | "Boot Data", | ||
455 | "Runtime Code", | ||
456 | "Runtime Data", | ||
457 | "Conventional Memory", | ||
458 | "Unusable Memory", | ||
459 | "ACPI Reclaim Memory", | ||
460 | "ACPI Memory NVS", | ||
461 | "Memory Mapped I/O", | ||
462 | "MMIO Port Space", | ||
463 | "PAL Code" | ||
464 | }; | ||
465 | |||
466 | char * __init efi_md_typeattr_format(char *buf, size_t size, | ||
467 | const efi_memory_desc_t *md) | ||
468 | { | ||
469 | char *pos; | ||
470 | int type_len; | ||
471 | u64 attr; | ||
472 | |||
473 | pos = buf; | ||
474 | if (md->type >= ARRAY_SIZE(memory_type_name)) | ||
475 | type_len = snprintf(pos, size, "[type=%u", md->type); | ||
476 | else | ||
477 | type_len = snprintf(pos, size, "[%-*s", | ||
478 | (int)(sizeof(memory_type_name[0]) - 1), | ||
479 | memory_type_name[md->type]); | ||
480 | if (type_len >= size) | ||
481 | return buf; | ||
482 | |||
483 | pos += type_len; | ||
484 | size -= type_len; | ||
485 | |||
486 | attr = md->attribute; | ||
487 | if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | | ||
488 | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP | | ||
489 | EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RUNTIME)) | ||
490 | snprintf(pos, size, "|attr=0x%016llx]", | ||
491 | (unsigned long long)attr); | ||
492 | else | ||
493 | snprintf(pos, size, "|%3s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", | ||
494 | attr & EFI_MEMORY_RUNTIME ? "RUN" : "", | ||
495 | attr & EFI_MEMORY_XP ? "XP" : "", | ||
496 | attr & EFI_MEMORY_RP ? "RP" : "", | ||
497 | attr & EFI_MEMORY_WP ? "WP" : "", | ||
498 | attr & EFI_MEMORY_UCE ? "UCE" : "", | ||
499 | attr & EFI_MEMORY_WB ? "WB" : "", | ||
500 | attr & EFI_MEMORY_WT ? "WT" : "", | ||
501 | attr & EFI_MEMORY_WC ? "WC" : "", | ||
502 | attr & EFI_MEMORY_UC ? "UC" : ""); | ||
503 | return buf; | ||
504 | } | ||
diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index 480339b6b110..75ee05964cbc 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c | |||
@@ -226,6 +226,10 @@ unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table, | |||
226 | goto fail_free_image; | 226 | goto fail_free_image; |
227 | } | 227 | } |
228 | 228 | ||
229 | status = efi_parse_options(cmdline_ptr); | ||
230 | if (status != EFI_SUCCESS) | ||
231 | pr_efi_err(sys_table, "Failed to parse EFI cmdline options\n"); | ||
232 | |||
229 | /* | 233 | /* |
230 | * Unauthenticated device tree data is a security hazard, so | 234 | * Unauthenticated device tree data is a security hazard, so |
231 | * ignore 'dtb=' unless UEFI Secure Boot is disabled. | 235 | * ignore 'dtb=' unless UEFI Secure Boot is disabled. |
diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 32d5cca30f49..a920fec8fe88 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c | |||
@@ -15,8 +15,23 @@ | |||
15 | 15 | ||
16 | #include "efistub.h" | 16 | #include "efistub.h" |
17 | 17 | ||
18 | /* | ||
19 | * Some firmware implementations have problems reading files in one go. | ||
20 | * A read chunk size of 1MB seems to work for most platforms. | ||
21 | * | ||
22 | * Unfortunately, reading files in chunks triggers *other* bugs on some | ||
23 | * platforms, so we provide a way to disable this workaround, which can | ||
24 | * be done by passing "efi=nochunk" on the EFI boot stub command line. | ||
25 | * | ||
26 | * If you experience issues with initrd images being corrupt it's worth | ||
27 | * trying efi=nochunk, but chunking is enabled by default because there | ||
28 | * are far more machines that require the workaround than those that | ||
29 | * break with it enabled. | ||
30 | */ | ||
18 | #define EFI_READ_CHUNK_SIZE (1024 * 1024) | 31 | #define EFI_READ_CHUNK_SIZE (1024 * 1024) |
19 | 32 | ||
33 | static unsigned long __chunk_size = EFI_READ_CHUNK_SIZE; | ||
34 | |||
20 | struct file_info { | 35 | struct file_info { |
21 | efi_file_handle_t *handle; | 36 | efi_file_handle_t *handle; |
22 | u64 size; | 37 | u64 size; |
@@ -281,6 +296,49 @@ void efi_free(efi_system_table_t *sys_table_arg, unsigned long size, | |||
281 | efi_call_early(free_pages, addr, nr_pages); | 296 | efi_call_early(free_pages, addr, nr_pages); |
282 | } | 297 | } |
283 | 298 | ||
299 | /* | ||
300 | * Parse the ASCII string 'cmdline' for EFI options, denoted by the efi= | ||
301 | * option, e.g. efi=nochunk. | ||
302 | * | ||
303 | * It should be noted that efi= is parsed in two very different | ||
304 | * environments, first in the early boot environment of the EFI boot | ||
305 | * stub, and subsequently during the kernel boot. | ||
306 | */ | ||
307 | efi_status_t efi_parse_options(char *cmdline) | ||
308 | { | ||
309 | char *str; | ||
310 | |||
311 | /* | ||
312 | * If no EFI parameters were specified on the cmdline we've got | ||
313 | * nothing to do. | ||
314 | */ | ||
315 | str = strstr(cmdline, "efi="); | ||
316 | if (!str) | ||
317 | return EFI_SUCCESS; | ||
318 | |||
319 | /* Skip ahead to first argument */ | ||
320 | str += strlen("efi="); | ||
321 | |||
322 | /* | ||
323 | * Remember, because efi= is also used by the kernel we need to | ||
324 | * skip over arguments we don't understand. | ||
325 | */ | ||
326 | while (*str) { | ||
327 | if (!strncmp(str, "nochunk", 7)) { | ||
328 | str += strlen("nochunk"); | ||
329 | __chunk_size = -1UL; | ||
330 | } | ||
331 | |||
332 | /* Group words together, delimited by "," */ | ||
333 | while (*str && *str != ',') | ||
334 | str++; | ||
335 | |||
336 | if (*str == ',') | ||
337 | str++; | ||
338 | } | ||
339 | |||
340 | return EFI_SUCCESS; | ||
341 | } | ||
284 | 342 | ||
285 | /* | 343 | /* |
286 | * Check the cmdline for a LILO-style file= arguments. | 344 | * Check the cmdline for a LILO-style file= arguments. |
@@ -423,8 +481,8 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, | |||
423 | size = files[j].size; | 481 | size = files[j].size; |
424 | while (size) { | 482 | while (size) { |
425 | unsigned long chunksize; | 483 | unsigned long chunksize; |
426 | if (size > EFI_READ_CHUNK_SIZE) | 484 | if (size > __chunk_size) |
427 | chunksize = EFI_READ_CHUNK_SIZE; | 485 | chunksize = __chunk_size; |
428 | else | 486 | else |
429 | chunksize = size; | 487 | chunksize = size; |
430 | 488 | ||
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 10daa4bbb258..228bbf910461 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c | |||
@@ -14,11 +14,80 @@ | |||
14 | * This file is released under the GPLv2. | 14 | * This file is released under the GPLv2. |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include <linux/bug.h> | ||
17 | #include <linux/efi.h> | 18 | #include <linux/efi.h> |
18 | #include <linux/spinlock.h> /* spinlock_t */ | 19 | #include <linux/mutex.h> |
20 | #include <linux/spinlock.h> | ||
19 | #include <asm/efi.h> | 21 | #include <asm/efi.h> |
20 | 22 | ||
21 | /* | 23 | /* |
24 | * According to section 7.1 of the UEFI spec, Runtime Services are not fully | ||
25 | * reentrant, and there are particular combinations of calls that need to be | ||
26 | * serialized. (source: UEFI Specification v2.4A) | ||
27 | * | ||
28 | * Table 31. Rules for Reentry Into Runtime Services | ||
29 | * +------------------------------------+-------------------------------+ | ||
30 | * | If previous call is busy in | Forbidden to call | | ||
31 | * +------------------------------------+-------------------------------+ | ||
32 | * | Any | SetVirtualAddressMap() | | ||
33 | * +------------------------------------+-------------------------------+ | ||
34 | * | ConvertPointer() | ConvertPointer() | | ||
35 | * +------------------------------------+-------------------------------+ | ||
36 | * | SetVariable() | ResetSystem() | | ||
37 | * | UpdateCapsule() | | | ||
38 | * | SetTime() | | | ||
39 | * | SetWakeupTime() | | | ||
40 | * | GetNextHighMonotonicCount() | | | ||
41 | * +------------------------------------+-------------------------------+ | ||
42 | * | GetVariable() | GetVariable() | | ||
43 | * | GetNextVariableName() | GetNextVariableName() | | ||
44 | * | SetVariable() | SetVariable() | | ||
45 | * | QueryVariableInfo() | QueryVariableInfo() | | ||
46 | * | UpdateCapsule() | UpdateCapsule() | | ||
47 | * | QueryCapsuleCapabilities() | QueryCapsuleCapabilities() | | ||
48 | * | GetNextHighMonotonicCount() | GetNextHighMonotonicCount() | | ||
49 | * +------------------------------------+-------------------------------+ | ||
50 | * | GetTime() | GetTime() | | ||
51 | * | SetTime() | SetTime() | | ||
52 | * | GetWakeupTime() | GetWakeupTime() | | ||
53 | * | SetWakeupTime() | SetWakeupTime() | | ||
54 | * +------------------------------------+-------------------------------+ | ||
55 | * | ||
56 | * Due to the fact that the EFI pstore may write to the variable store in | ||
57 | * interrupt context, we need to use a spinlock for at least the groups that | ||
58 | * contain SetVariable() and QueryVariableInfo(). That leaves little else, as | ||
59 | * none of the remaining functions are actually ever called at runtime. | ||
60 | * So let's just use a single spinlock to serialize all Runtime Services calls. | ||
61 | */ | ||
62 | static DEFINE_SPINLOCK(efi_runtime_lock); | ||
63 | |||
64 | /* | ||
65 | * Some runtime services calls can be reentrant under NMI, even if the table | ||
66 | * above says they are not. (source: UEFI Specification v2.4A) | ||
67 | * | ||
68 | * Table 32. Functions that may be called after Machine Check, INIT and NMI | ||
69 | * +----------------------------+------------------------------------------+ | ||
70 | * | Function | Called after Machine Check, INIT and NMI | | ||
71 | * +----------------------------+------------------------------------------+ | ||
72 | * | GetTime() | Yes, even if previously busy. | | ||
73 | * | GetVariable() | Yes, even if previously busy | | ||
74 | * | GetNextVariableName() | Yes, even if previously busy | | ||
75 | * | QueryVariableInfo() | Yes, even if previously busy | | ||
76 | * | SetVariable() | Yes, even if previously busy | | ||
77 | * | UpdateCapsule() | Yes, even if previously busy | | ||
78 | * | QueryCapsuleCapabilities() | Yes, even if previously busy | | ||
79 | * | ResetSystem() | Yes, even if previously busy | | ||
80 | * +----------------------------+------------------------------------------+ | ||
81 | * | ||
82 | * In order to prevent deadlocks under NMI, the wrappers for these functions | ||
83 | * may only grab the efi_runtime_lock or rtc_lock spinlocks if !efi_in_nmi(). | ||
84 | * However, not all of the services listed are reachable through NMI code paths, | ||
85 | * so the the special handling as suggested by the UEFI spec is only implemented | ||
86 | * for QueryVariableInfo() and SetVariable(), as these can be reached in NMI | ||
87 | * context through efi_pstore_write(). | ||
88 | */ | ||
89 | |||
90 | /* | ||
22 | * As per commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock"), | 91 | * As per commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock"), |
23 | * the EFI specification requires that callers of the time related runtime | 92 | * the EFI specification requires that callers of the time related runtime |
24 | * functions serialize with other CMOS accesses in the kernel, as the EFI time | 93 | * functions serialize with other CMOS accesses in the kernel, as the EFI time |
@@ -32,7 +101,9 @@ static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) | |||
32 | efi_status_t status; | 101 | efi_status_t status; |
33 | 102 | ||
34 | spin_lock_irqsave(&rtc_lock, flags); | 103 | spin_lock_irqsave(&rtc_lock, flags); |
104 | spin_lock(&efi_runtime_lock); | ||
35 | status = efi_call_virt(get_time, tm, tc); | 105 | status = efi_call_virt(get_time, tm, tc); |
106 | spin_unlock(&efi_runtime_lock); | ||
36 | spin_unlock_irqrestore(&rtc_lock, flags); | 107 | spin_unlock_irqrestore(&rtc_lock, flags); |
37 | return status; | 108 | return status; |
38 | } | 109 | } |
@@ -43,7 +114,9 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm) | |||
43 | efi_status_t status; | 114 | efi_status_t status; |
44 | 115 | ||
45 | spin_lock_irqsave(&rtc_lock, flags); | 116 | spin_lock_irqsave(&rtc_lock, flags); |
117 | spin_lock(&efi_runtime_lock); | ||
46 | status = efi_call_virt(set_time, tm); | 118 | status = efi_call_virt(set_time, tm); |
119 | spin_unlock(&efi_runtime_lock); | ||
47 | spin_unlock_irqrestore(&rtc_lock, flags); | 120 | spin_unlock_irqrestore(&rtc_lock, flags); |
48 | return status; | 121 | return status; |
49 | } | 122 | } |
@@ -56,7 +129,9 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, | |||
56 | efi_status_t status; | 129 | efi_status_t status; |
57 | 130 | ||
58 | spin_lock_irqsave(&rtc_lock, flags); | 131 | spin_lock_irqsave(&rtc_lock, flags); |
132 | spin_lock(&efi_runtime_lock); | ||
59 | status = efi_call_virt(get_wakeup_time, enabled, pending, tm); | 133 | status = efi_call_virt(get_wakeup_time, enabled, pending, tm); |
134 | spin_unlock(&efi_runtime_lock); | ||
60 | spin_unlock_irqrestore(&rtc_lock, flags); | 135 | spin_unlock_irqrestore(&rtc_lock, flags); |
61 | return status; | 136 | return status; |
62 | } | 137 | } |
@@ -67,7 +142,9 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) | |||
67 | efi_status_t status; | 142 | efi_status_t status; |
68 | 143 | ||
69 | spin_lock_irqsave(&rtc_lock, flags); | 144 | spin_lock_irqsave(&rtc_lock, flags); |
145 | spin_lock(&efi_runtime_lock); | ||
70 | status = efi_call_virt(set_wakeup_time, enabled, tm); | 146 | status = efi_call_virt(set_wakeup_time, enabled, tm); |
147 | spin_unlock(&efi_runtime_lock); | ||
71 | spin_unlock_irqrestore(&rtc_lock, flags); | 148 | spin_unlock_irqrestore(&rtc_lock, flags); |
72 | return status; | 149 | return status; |
73 | } | 150 | } |
@@ -78,14 +155,27 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name, | |||
78 | unsigned long *data_size, | 155 | unsigned long *data_size, |
79 | void *data) | 156 | void *data) |
80 | { | 157 | { |
81 | return efi_call_virt(get_variable, name, vendor, attr, data_size, data); | 158 | unsigned long flags; |
159 | efi_status_t status; | ||
160 | |||
161 | spin_lock_irqsave(&efi_runtime_lock, flags); | ||
162 | status = efi_call_virt(get_variable, name, vendor, attr, data_size, | ||
163 | data); | ||
164 | spin_unlock_irqrestore(&efi_runtime_lock, flags); | ||
165 | return status; | ||
82 | } | 166 | } |
83 | 167 | ||
84 | static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, | 168 | static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, |
85 | efi_char16_t *name, | 169 | efi_char16_t *name, |
86 | efi_guid_t *vendor) | 170 | efi_guid_t *vendor) |
87 | { | 171 | { |
88 | return efi_call_virt(get_next_variable, name_size, name, vendor); | 172 | unsigned long flags; |
173 | efi_status_t status; | ||
174 | |||
175 | spin_lock_irqsave(&efi_runtime_lock, flags); | ||
176 | status = efi_call_virt(get_next_variable, name_size, name, vendor); | ||
177 | spin_unlock_irqrestore(&efi_runtime_lock, flags); | ||
178 | return status; | ||
89 | } | 179 | } |
90 | 180 | ||
91 | static efi_status_t virt_efi_set_variable(efi_char16_t *name, | 181 | static efi_status_t virt_efi_set_variable(efi_char16_t *name, |
@@ -94,24 +184,61 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, | |||
94 | unsigned long data_size, | 184 | unsigned long data_size, |
95 | void *data) | 185 | void *data) |
96 | { | 186 | { |
97 | return efi_call_virt(set_variable, name, vendor, attr, data_size, data); | 187 | unsigned long flags; |
188 | efi_status_t status; | ||
189 | |||
190 | spin_lock_irqsave(&efi_runtime_lock, flags); | ||
191 | status = efi_call_virt(set_variable, name, vendor, attr, data_size, | ||
192 | data); | ||
193 | spin_unlock_irqrestore(&efi_runtime_lock, flags); | ||
194 | return status; | ||
98 | } | 195 | } |
99 | 196 | ||
197 | static efi_status_t | ||
198 | virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor, | ||
199 | u32 attr, unsigned long data_size, | ||
200 | void *data) | ||
201 | { | ||
202 | unsigned long flags; | ||
203 | efi_status_t status; | ||
204 | |||
205 | if (!spin_trylock_irqsave(&efi_runtime_lock, flags)) | ||
206 | return EFI_NOT_READY; | ||
207 | |||
208 | status = efi_call_virt(set_variable, name, vendor, attr, data_size, | ||
209 | data); | ||
210 | spin_unlock_irqrestore(&efi_runtime_lock, flags); | ||
211 | return status; | ||
212 | } | ||
213 | |||
214 | |||
100 | static efi_status_t virt_efi_query_variable_info(u32 attr, | 215 | static efi_status_t virt_efi_query_variable_info(u32 attr, |
101 | u64 *storage_space, | 216 | u64 *storage_space, |
102 | u64 *remaining_space, | 217 | u64 *remaining_space, |
103 | u64 *max_variable_size) | 218 | u64 *max_variable_size) |
104 | { | 219 | { |
220 | unsigned long flags; | ||
221 | efi_status_t status; | ||
222 | |||
105 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) | 223 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
106 | return EFI_UNSUPPORTED; | 224 | return EFI_UNSUPPORTED; |
107 | 225 | ||
108 | return efi_call_virt(query_variable_info, attr, storage_space, | 226 | spin_lock_irqsave(&efi_runtime_lock, flags); |
109 | remaining_space, max_variable_size); | 227 | status = efi_call_virt(query_variable_info, attr, storage_space, |
228 | remaining_space, max_variable_size); | ||
229 | spin_unlock_irqrestore(&efi_runtime_lock, flags); | ||
230 | return status; | ||
110 | } | 231 | } |
111 | 232 | ||
112 | static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) | 233 | static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) |
113 | { | 234 | { |
114 | return efi_call_virt(get_next_high_mono_count, count); | 235 | unsigned long flags; |
236 | efi_status_t status; | ||
237 | |||
238 | spin_lock_irqsave(&efi_runtime_lock, flags); | ||
239 | status = efi_call_virt(get_next_high_mono_count, count); | ||
240 | spin_unlock_irqrestore(&efi_runtime_lock, flags); | ||
241 | return status; | ||
115 | } | 242 | } |
116 | 243 | ||
117 | static void virt_efi_reset_system(int reset_type, | 244 | static void virt_efi_reset_system(int reset_type, |
@@ -119,17 +246,27 @@ static void virt_efi_reset_system(int reset_type, | |||
119 | unsigned long data_size, | 246 | unsigned long data_size, |
120 | efi_char16_t *data) | 247 | efi_char16_t *data) |
121 | { | 248 | { |
249 | unsigned long flags; | ||
250 | |||
251 | spin_lock_irqsave(&efi_runtime_lock, flags); | ||
122 | __efi_call_virt(reset_system, reset_type, status, data_size, data); | 252 | __efi_call_virt(reset_system, reset_type, status, data_size, data); |
253 | spin_unlock_irqrestore(&efi_runtime_lock, flags); | ||
123 | } | 254 | } |
124 | 255 | ||
125 | static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, | 256 | static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, |
126 | unsigned long count, | 257 | unsigned long count, |
127 | unsigned long sg_list) | 258 | unsigned long sg_list) |
128 | { | 259 | { |
260 | unsigned long flags; | ||
261 | efi_status_t status; | ||
262 | |||
129 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) | 263 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
130 | return EFI_UNSUPPORTED; | 264 | return EFI_UNSUPPORTED; |
131 | 265 | ||
132 | return efi_call_virt(update_capsule, capsules, count, sg_list); | 266 | spin_lock_irqsave(&efi_runtime_lock, flags); |
267 | status = efi_call_virt(update_capsule, capsules, count, sg_list); | ||
268 | spin_unlock_irqrestore(&efi_runtime_lock, flags); | ||
269 | return status; | ||
133 | } | 270 | } |
134 | 271 | ||
135 | static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, | 272 | static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, |
@@ -137,11 +274,17 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, | |||
137 | u64 *max_size, | 274 | u64 *max_size, |
138 | int *reset_type) | 275 | int *reset_type) |
139 | { | 276 | { |
277 | unsigned long flags; | ||
278 | efi_status_t status; | ||
279 | |||
140 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) | 280 | if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) |
141 | return EFI_UNSUPPORTED; | 281 | return EFI_UNSUPPORTED; |
142 | 282 | ||
143 | return efi_call_virt(query_capsule_caps, capsules, count, max_size, | 283 | spin_lock_irqsave(&efi_runtime_lock, flags); |
144 | reset_type); | 284 | status = efi_call_virt(query_capsule_caps, capsules, count, max_size, |
285 | reset_type); | ||
286 | spin_unlock_irqrestore(&efi_runtime_lock, flags); | ||
287 | return status; | ||
145 | } | 288 | } |
146 | 289 | ||
147 | void efi_native_runtime_setup(void) | 290 | void efi_native_runtime_setup(void) |
@@ -153,6 +296,7 @@ void efi_native_runtime_setup(void) | |||
153 | efi.get_variable = virt_efi_get_variable; | 296 | efi.get_variable = virt_efi_get_variable; |
154 | efi.get_next_variable = virt_efi_get_next_variable; | 297 | efi.get_next_variable = virt_efi_get_next_variable; |
155 | efi.set_variable = virt_efi_set_variable; | 298 | efi.set_variable = virt_efi_set_variable; |
299 | efi.set_variable_nonblocking = virt_efi_set_variable_nonblocking; | ||
156 | efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; | 300 | efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; |
157 | efi.reset_system = virt_efi_reset_system; | 301 | efi.reset_system = virt_efi_reset_system; |
158 | efi.query_variable_info = virt_efi_query_variable_info; | 302 | efi.query_variable_info = virt_efi_query_variable_info; |
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 5abe943e3404..70a0fb10517f 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c | |||
@@ -321,11 +321,11 @@ static unsigned long var_name_strnsize(efi_char16_t *variable_name, | |||
321 | * Print a warning when duplicate EFI variables are encountered and | 321 | * Print a warning when duplicate EFI variables are encountered and |
322 | * disable the sysfs workqueue since the firmware is buggy. | 322 | * disable the sysfs workqueue since the firmware is buggy. |
323 | */ | 323 | */ |
324 | static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, | 324 | static void dup_variable_bug(efi_char16_t *str16, efi_guid_t *vendor_guid, |
325 | unsigned long len16) | 325 | unsigned long len16) |
326 | { | 326 | { |
327 | size_t i, len8 = len16 / sizeof(efi_char16_t); | 327 | size_t i, len8 = len16 / sizeof(efi_char16_t); |
328 | char *s8; | 328 | char *str8; |
329 | 329 | ||
330 | /* | 330 | /* |
331 | * Disable the workqueue since the algorithm it uses for | 331 | * Disable the workqueue since the algorithm it uses for |
@@ -334,16 +334,16 @@ static void dup_variable_bug(efi_char16_t *s16, efi_guid_t *vendor_guid, | |||
334 | */ | 334 | */ |
335 | efivar_wq_enabled = false; | 335 | efivar_wq_enabled = false; |
336 | 336 | ||
337 | s8 = kzalloc(len8, GFP_KERNEL); | 337 | str8 = kzalloc(len8, GFP_KERNEL); |
338 | if (!s8) | 338 | if (!str8) |
339 | return; | 339 | return; |
340 | 340 | ||
341 | for (i = 0; i < len8; i++) | 341 | for (i = 0; i < len8; i++) |
342 | s8[i] = s16[i]; | 342 | str8[i] = str16[i]; |
343 | 343 | ||
344 | printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n", | 344 | printk(KERN_WARNING "efivars: duplicate variable: %s-%pUl\n", |
345 | s8, vendor_guid); | 345 | str8, vendor_guid); |
346 | kfree(s8); | 346 | kfree(str8); |
347 | } | 347 | } |
348 | 348 | ||
349 | /** | 349 | /** |
@@ -595,6 +595,39 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes, | |||
595 | } | 595 | } |
596 | EXPORT_SYMBOL_GPL(efivar_entry_set); | 596 | EXPORT_SYMBOL_GPL(efivar_entry_set); |
597 | 597 | ||
598 | /* | ||
599 | * efivar_entry_set_nonblocking - call set_variable_nonblocking() | ||
600 | * | ||
601 | * This function is guaranteed to not block and is suitable for calling | ||
602 | * from crash/panic handlers. | ||
603 | * | ||
604 | * Crucially, this function will not block if it cannot acquire | ||
605 | * __efivars->lock. Instead, it returns -EBUSY. | ||
606 | */ | ||
607 | static int | ||
608 | efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor, | ||
609 | u32 attributes, unsigned long size, void *data) | ||
610 | { | ||
611 | const struct efivar_operations *ops = __efivars->ops; | ||
612 | unsigned long flags; | ||
613 | efi_status_t status; | ||
614 | |||
615 | if (!spin_trylock_irqsave(&__efivars->lock, flags)) | ||
616 | return -EBUSY; | ||
617 | |||
618 | status = check_var_size(attributes, size + ucs2_strsize(name, 1024)); | ||
619 | if (status != EFI_SUCCESS) { | ||
620 | spin_unlock_irqrestore(&__efivars->lock, flags); | ||
621 | return -ENOSPC; | ||
622 | } | ||
623 | |||
624 | status = ops->set_variable_nonblocking(name, &vendor, attributes, | ||
625 | size, data); | ||
626 | |||
627 | spin_unlock_irqrestore(&__efivars->lock, flags); | ||
628 | return efi_status_to_err(status); | ||
629 | } | ||
630 | |||
598 | /** | 631 | /** |
599 | * efivar_entry_set_safe - call set_variable() if enough space in firmware | 632 | * efivar_entry_set_safe - call set_variable() if enough space in firmware |
600 | * @name: buffer containing the variable name | 633 | * @name: buffer containing the variable name |
@@ -622,6 +655,20 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes, | |||
622 | if (!ops->query_variable_store) | 655 | if (!ops->query_variable_store) |
623 | return -ENOSYS; | 656 | return -ENOSYS; |
624 | 657 | ||
658 | /* | ||
659 | * If the EFI variable backend provides a non-blocking | ||
660 | * ->set_variable() operation and we're in a context where we | ||
661 | * cannot block, then we need to use it to avoid live-locks, | ||
662 | * since the implication is that the regular ->set_variable() | ||
663 | * will block. | ||
664 | * | ||
665 | * If no ->set_variable_nonblocking() is provided then | ||
666 | * ->set_variable() is assumed to be non-blocking. | ||
667 | */ | ||
668 | if (!block && ops->set_variable_nonblocking) | ||
669 | return efivar_entry_set_nonblocking(name, vendor, attributes, | ||
670 | size, data); | ||
671 | |||
625 | if (!block) { | 672 | if (!block) { |
626 | if (!spin_trylock_irqsave(&__efivars->lock, flags)) | 673 | if (!spin_trylock_irqsave(&__efivars->lock, flags)) |
627 | return -EBUSY; | 674 | return -EBUSY; |
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index e705335101a5..c2a1cba1e984 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c | |||
@@ -32,6 +32,8 @@ static struct drm_driver driver; | |||
32 | static const struct pci_device_id pciidlist[] = { | 32 | static const struct pci_device_id pciidlist[] = { |
33 | { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, 0x1af4, 0x1100, 0, | 33 | { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, 0x1af4, 0x1100, 0, |
34 | 0, 0 }, | 34 | 0, 0 }, |
35 | { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, PCI_VENDOR_ID_XEN, | ||
36 | 0x0001, 0, 0, 0 }, | ||
35 | {0,} | 37 | {0,} |
36 | }; | 38 | }; |
37 | 39 | ||
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3201986bf25e..f66392b6e287 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
@@ -1711,7 +1711,7 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, | |||
1711 | #define HPD_STORM_DETECT_PERIOD 1000 | 1711 | #define HPD_STORM_DETECT_PERIOD 1000 |
1712 | #define HPD_STORM_THRESHOLD 5 | 1712 | #define HPD_STORM_THRESHOLD 5 |
1713 | 1713 | ||
1714 | static int ilk_port_to_hotplug_shift(enum port port) | 1714 | static int pch_port_to_hotplug_shift(enum port port) |
1715 | { | 1715 | { |
1716 | switch (port) { | 1716 | switch (port) { |
1717 | case PORT_A: | 1717 | case PORT_A: |
@@ -1727,7 +1727,7 @@ static int ilk_port_to_hotplug_shift(enum port port) | |||
1727 | } | 1727 | } |
1728 | } | 1728 | } |
1729 | 1729 | ||
1730 | static int g4x_port_to_hotplug_shift(enum port port) | 1730 | static int i915_port_to_hotplug_shift(enum port port) |
1731 | { | 1731 | { |
1732 | switch (port) { | 1732 | switch (port) { |
1733 | case PORT_A: | 1733 | case PORT_A: |
@@ -1785,12 +1785,12 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, | |||
1785 | if (port && dev_priv->hpd_irq_port[port]) { | 1785 | if (port && dev_priv->hpd_irq_port[port]) { |
1786 | bool long_hpd; | 1786 | bool long_hpd; |
1787 | 1787 | ||
1788 | if (IS_G4X(dev)) { | 1788 | if (HAS_PCH_SPLIT(dev)) { |
1789 | dig_shift = g4x_port_to_hotplug_shift(port); | 1789 | dig_shift = pch_port_to_hotplug_shift(port); |
1790 | long_hpd = (hotplug_trigger >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT; | ||
1791 | } else { | ||
1792 | dig_shift = ilk_port_to_hotplug_shift(port); | ||
1793 | long_hpd = (dig_hotplug_reg >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT; | 1790 | long_hpd = (dig_hotplug_reg >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT; |
1791 | } else { | ||
1792 | dig_shift = i915_port_to_hotplug_shift(port); | ||
1793 | long_hpd = (hotplug_trigger >> dig_shift) & PORTB_HOTPLUG_LONG_DETECT; | ||
1794 | } | 1794 | } |
1795 | 1795 | ||
1796 | DRM_DEBUG_DRIVER("digital hpd port %c - %s\n", | 1796 | DRM_DEBUG_DRIVER("digital hpd port %c - %s\n", |
@@ -3458,12 +3458,13 @@ static void gen8_irq_reset(struct drm_device *dev) | |||
3458 | void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv) | 3458 | void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv) |
3459 | { | 3459 | { |
3460 | unsigned long irqflags; | 3460 | unsigned long irqflags; |
3461 | uint32_t extra_ier = GEN8_PIPE_VBLANK | GEN8_PIPE_FIFO_UNDERRUN; | ||
3461 | 3462 | ||
3462 | spin_lock_irqsave(&dev_priv->irq_lock, irqflags); | 3463 | spin_lock_irqsave(&dev_priv->irq_lock, irqflags); |
3463 | GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_B, dev_priv->de_irq_mask[PIPE_B], | 3464 | GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_B, dev_priv->de_irq_mask[PIPE_B], |
3464 | ~dev_priv->de_irq_mask[PIPE_B]); | 3465 | ~dev_priv->de_irq_mask[PIPE_B] | extra_ier); |
3465 | GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_C, dev_priv->de_irq_mask[PIPE_C], | 3466 | GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_C, dev_priv->de_irq_mask[PIPE_C], |
3466 | ~dev_priv->de_irq_mask[PIPE_C]); | 3467 | ~dev_priv->de_irq_mask[PIPE_C] | extra_ier); |
3467 | spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); | 3468 | spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); |
3468 | } | 3469 | } |
3469 | 3470 | ||
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 507370513f3d..c9e220963a78 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
@@ -73,9 +73,6 @@ static const uint32_t intel_cursor_formats[] = { | |||
73 | DRM_FORMAT_ARGB8888, | 73 | DRM_FORMAT_ARGB8888, |
74 | }; | 74 | }; |
75 | 75 | ||
76 | #define DIV_ROUND_CLOSEST_ULL(ll, d) \ | ||
77 | ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) | ||
78 | |||
79 | static void intel_increase_pllclock(struct drm_device *dev, | 76 | static void intel_increase_pllclock(struct drm_device *dev, |
80 | enum pipe pipe); | 77 | enum pipe pipe); |
81 | static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); | 78 | static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); |
@@ -12357,27 +12354,36 @@ static void intel_setup_outputs(struct drm_device *dev) | |||
12357 | if (I915_READ(PCH_DP_D) & DP_DETECTED) | 12354 | if (I915_READ(PCH_DP_D) & DP_DETECTED) |
12358 | intel_dp_init(dev, PCH_DP_D, PORT_D); | 12355 | intel_dp_init(dev, PCH_DP_D, PORT_D); |
12359 | } else if (IS_VALLEYVIEW(dev)) { | 12356 | } else if (IS_VALLEYVIEW(dev)) { |
12360 | if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED) { | 12357 | /* |
12358 | * The DP_DETECTED bit is the latched state of the DDC | ||
12359 | * SDA pin at boot. However since eDP doesn't require DDC | ||
12360 | * (no way to plug in a DP->HDMI dongle) the DDC pins for | ||
12361 | * eDP ports may have been muxed to an alternate function. | ||
12362 | * Thus we can't rely on the DP_DETECTED bit alone to detect | ||
12363 | * eDP ports. Consult the VBT as well as DP_DETECTED to | ||
12364 | * detect eDP ports. | ||
12365 | */ | ||
12366 | if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED) | ||
12361 | intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIB, | 12367 | intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIB, |
12362 | PORT_B); | 12368 | PORT_B); |
12363 | if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED) | 12369 | if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED || |
12364 | intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B); | 12370 | intel_dp_is_edp(dev, PORT_B)) |
12365 | } | 12371 | intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B); |
12366 | 12372 | ||
12367 | if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIC) & SDVO_DETECTED) { | 12373 | if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIC) & SDVO_DETECTED) |
12368 | intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIC, | 12374 | intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIC, |
12369 | PORT_C); | 12375 | PORT_C); |
12370 | if (I915_READ(VLV_DISPLAY_BASE + DP_C) & DP_DETECTED) | 12376 | if (I915_READ(VLV_DISPLAY_BASE + DP_C) & DP_DETECTED || |
12371 | intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C); | 12377 | intel_dp_is_edp(dev, PORT_C)) |
12372 | } | 12378 | intel_dp_init(dev, VLV_DISPLAY_BASE + DP_C, PORT_C); |
12373 | 12379 | ||
12374 | if (IS_CHERRYVIEW(dev)) { | 12380 | if (IS_CHERRYVIEW(dev)) { |
12375 | if (I915_READ(VLV_DISPLAY_BASE + CHV_HDMID) & SDVO_DETECTED) { | 12381 | if (I915_READ(VLV_DISPLAY_BASE + CHV_HDMID) & SDVO_DETECTED) |
12376 | intel_hdmi_init(dev, VLV_DISPLAY_BASE + CHV_HDMID, | 12382 | intel_hdmi_init(dev, VLV_DISPLAY_BASE + CHV_HDMID, |
12377 | PORT_D); | 12383 | PORT_D); |
12378 | if (I915_READ(VLV_DISPLAY_BASE + DP_D) & DP_DETECTED) | 12384 | /* eDP not supported on port D, so don't check VBT */ |
12379 | intel_dp_init(dev, VLV_DISPLAY_BASE + DP_D, PORT_D); | 12385 | if (I915_READ(VLV_DISPLAY_BASE + DP_D) & DP_DETECTED) |
12380 | } | 12386 | intel_dp_init(dev, VLV_DISPLAY_BASE + DP_D, PORT_D); |
12381 | } | 12387 | } |
12382 | 12388 | ||
12383 | intel_dsi_init(dev); | 12389 | intel_dsi_init(dev); |
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 07ce04683c30..ba715229a540 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h | |||
@@ -35,6 +35,9 @@ | |||
35 | #include <drm/drm_fb_helper.h> | 35 | #include <drm/drm_fb_helper.h> |
36 | #include <drm/drm_dp_mst_helper.h> | 36 | #include <drm/drm_dp_mst_helper.h> |
37 | 37 | ||
38 | #define DIV_ROUND_CLOSEST_ULL(ll, d) \ | ||
39 | ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) | ||
40 | |||
38 | /** | 41 | /** |
39 | * _wait_for - magic (register) wait macro | 42 | * _wait_for - magic (register) wait macro |
40 | * | 43 | * |
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 18784470a760..0e018cb49147 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c | |||
@@ -419,9 +419,8 @@ static uint32_t scale(uint32_t source_val, | |||
419 | source_val = clamp(source_val, source_min, source_max); | 419 | source_val = clamp(source_val, source_min, source_max); |
420 | 420 | ||
421 | /* avoid overflows */ | 421 | /* avoid overflows */ |
422 | target_val = (uint64_t)(source_val - source_min) * | 422 | target_val = DIV_ROUND_CLOSEST_ULL((uint64_t)(source_val - source_min) * |
423 | (target_max - target_min); | 423 | (target_max - target_min), source_max - source_min); |
424 | do_div(target_val, source_max - source_min); | ||
425 | target_val += target_min; | 424 | target_val += target_min; |
426 | 425 | ||
427 | return target_val; | 426 | return target_val; |
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c index 552fdbd45ebe..1d0e33fb5f61 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnv50.c | |||
@@ -113,6 +113,8 @@ | |||
113 | #define IS_NVA3F(x) (((x) > 0xa0 && (x) < 0xaa) || (x) == 0xaf) | 113 | #define IS_NVA3F(x) (((x) > 0xa0 && (x) < 0xaa) || (x) == 0xaf) |
114 | #define IS_NVAAF(x) ((x) >= 0xaa && (x) <= 0xac) | 114 | #define IS_NVAAF(x) ((x) >= 0xaa && (x) <= 0xac) |
115 | 115 | ||
116 | #include <subdev/fb.h> | ||
117 | |||
116 | /* | 118 | /* |
117 | * This code deals with PGRAPH contexts on NV50 family cards. Like NV40, it's | 119 | * This code deals with PGRAPH contexts on NV50 family cards. Like NV40, it's |
118 | * the GPU itself that does context-switching, but it needs a special | 120 | * the GPU itself that does context-switching, but it needs a special |
@@ -569,8 +571,12 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) | |||
569 | gr_def(ctx, 0x407d08, 0x00010040); | 571 | gr_def(ctx, 0x407d08, 0x00010040); |
570 | else if (device->chipset < 0xa0) | 572 | else if (device->chipset < 0xa0) |
571 | gr_def(ctx, 0x407d08, 0x00390040); | 573 | gr_def(ctx, 0x407d08, 0x00390040); |
572 | else | 574 | else { |
573 | gr_def(ctx, 0x407d08, 0x003d0040); | 575 | if (nouveau_fb(device)->ram->type != NV_MEM_TYPE_GDDR5) |
576 | gr_def(ctx, 0x407d08, 0x003d0040); | ||
577 | else | ||
578 | gr_def(ctx, 0x407d08, 0x003c0040); | ||
579 | } | ||
574 | gr_def(ctx, 0x407d0c, 0x00000022); | 580 | gr_def(ctx, 0x407d0c, 0x00000022); |
575 | } | 581 | } |
576 | 582 | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index 589dbb582da2..fd3dbd59d73e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c | |||
@@ -400,15 +400,20 @@ nouveau_channel_new(struct nouveau_drm *drm, struct nvif_device *device, | |||
400 | struct nouveau_channel **pchan) | 400 | struct nouveau_channel **pchan) |
401 | { | 401 | { |
402 | struct nouveau_cli *cli = (void *)nvif_client(&device->base); | 402 | struct nouveau_cli *cli = (void *)nvif_client(&device->base); |
403 | bool super; | ||
403 | int ret; | 404 | int ret; |
404 | 405 | ||
406 | /* hack until fencenv50 is fixed, and agp access relaxed */ | ||
407 | super = cli->base.super; | ||
408 | cli->base.super = true; | ||
409 | |||
405 | ret = nouveau_channel_ind(drm, device, handle, arg0, pchan); | 410 | ret = nouveau_channel_ind(drm, device, handle, arg0, pchan); |
406 | if (ret) { | 411 | if (ret) { |
407 | NV_PRINTK(debug, cli, "ib channel create, %d\n", ret); | 412 | NV_PRINTK(debug, cli, "ib channel create, %d\n", ret); |
408 | ret = nouveau_channel_dma(drm, device, handle, pchan); | 413 | ret = nouveau_channel_dma(drm, device, handle, pchan); |
409 | if (ret) { | 414 | if (ret) { |
410 | NV_PRINTK(debug, cli, "dma channel create, %d\n", ret); | 415 | NV_PRINTK(debug, cli, "dma channel create, %d\n", ret); |
411 | return ret; | 416 | goto done; |
412 | } | 417 | } |
413 | } | 418 | } |
414 | 419 | ||
@@ -416,8 +421,9 @@ nouveau_channel_new(struct nouveau_drm *drm, struct nvif_device *device, | |||
416 | if (ret) { | 421 | if (ret) { |
417 | NV_PRINTK(error, cli, "channel failed to initialise, %d\n", ret); | 422 | NV_PRINTK(error, cli, "channel failed to initialise, %d\n", ret); |
418 | nouveau_channel_del(pchan); | 423 | nouveau_channel_del(pchan); |
419 | return ret; | ||
420 | } | 424 | } |
421 | 425 | ||
422 | return 0; | 426 | done: |
427 | cli->base.super = super; | ||
428 | return ret; | ||
423 | } | 429 | } |
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index af9e78546688..0d1396266857 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c | |||
@@ -572,7 +572,6 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, | |||
572 | struct qxl_framebuffer *qfb; | 572 | struct qxl_framebuffer *qfb; |
573 | struct qxl_bo *bo, *old_bo = NULL; | 573 | struct qxl_bo *bo, *old_bo = NULL; |
574 | struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); | 574 | struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); |
575 | uint32_t width, height, base_offset; | ||
576 | bool recreate_primary = false; | 575 | bool recreate_primary = false; |
577 | int ret; | 576 | int ret; |
578 | int surf_id; | 577 | int surf_id; |
@@ -602,9 +601,10 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, | |||
602 | if (qcrtc->index == 0) | 601 | if (qcrtc->index == 0) |
603 | recreate_primary = true; | 602 | recreate_primary = true; |
604 | 603 | ||
605 | width = mode->hdisplay; | 604 | if (bo->surf.stride * bo->surf.height > qdev->vram_size) { |
606 | height = mode->vdisplay; | 605 | DRM_ERROR("Mode doesn't fit in vram size (vgamem)"); |
607 | base_offset = 0; | 606 | return -EINVAL; |
607 | } | ||
608 | 608 | ||
609 | ret = qxl_bo_reserve(bo, false); | 609 | ret = qxl_bo_reserve(bo, false); |
610 | if (ret != 0) | 610 | if (ret != 0) |
@@ -618,10 +618,10 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, | |||
618 | if (recreate_primary) { | 618 | if (recreate_primary) { |
619 | qxl_io_destroy_primary(qdev); | 619 | qxl_io_destroy_primary(qdev); |
620 | qxl_io_log(qdev, | 620 | qxl_io_log(qdev, |
621 | "recreate primary: %dx%d (was %dx%d,%d,%d)\n", | 621 | "recreate primary: %dx%d,%d,%d\n", |
622 | width, height, bo->surf.width, | 622 | bo->surf.width, bo->surf.height, |
623 | bo->surf.height, bo->surf.stride, bo->surf.format); | 623 | bo->surf.stride, bo->surf.format); |
624 | qxl_io_create_primary(qdev, base_offset, bo); | 624 | qxl_io_create_primary(qdev, 0, bo); |
625 | bo->is_primary = true; | 625 | bo->is_primary = true; |
626 | } | 626 | } |
627 | 627 | ||
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 300d971187c4..0b2929de9f41 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include "drmP.h" | 25 | #include "drmP.h" |
26 | #include "radeon.h" | 26 | #include "radeon.h" |
27 | #include "radeon_asic.h" | ||
27 | #include "btcd.h" | 28 | #include "btcd.h" |
28 | #include "r600_dpm.h" | 29 | #include "r600_dpm.h" |
29 | #include "cypress_dpm.h" | 30 | #include "cypress_dpm.h" |
@@ -1170,6 +1171,23 @@ static const struct radeon_blacklist_clocks btc_blacklist_clocks[] = | |||
1170 | { 25000, 30000, RADEON_SCLK_UP } | 1171 | { 25000, 30000, RADEON_SCLK_UP } |
1171 | }; | 1172 | }; |
1172 | 1173 | ||
1174 | void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table, | ||
1175 | u32 *max_clock) | ||
1176 | { | ||
1177 | u32 i, clock = 0; | ||
1178 | |||
1179 | if ((table == NULL) || (table->count == 0)) { | ||
1180 | *max_clock = clock; | ||
1181 | return; | ||
1182 | } | ||
1183 | |||
1184 | for (i = 0; i < table->count; i++) { | ||
1185 | if (clock < table->entries[i].clk) | ||
1186 | clock = table->entries[i].clk; | ||
1187 | } | ||
1188 | *max_clock = clock; | ||
1189 | } | ||
1190 | |||
1173 | void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, | 1191 | void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, |
1174 | u32 clock, u16 max_voltage, u16 *voltage) | 1192 | u32 clock, u16 max_voltage, u16 *voltage) |
1175 | { | 1193 | { |
diff --git a/drivers/gpu/drm/radeon/btc_dpm.h b/drivers/gpu/drm/radeon/btc_dpm.h index 1a15e0e41950..3b6f12b7760b 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.h +++ b/drivers/gpu/drm/radeon/btc_dpm.h | |||
@@ -46,6 +46,8 @@ void btc_adjust_clock_combinations(struct radeon_device *rdev, | |||
46 | struct rv7xx_pl *pl); | 46 | struct rv7xx_pl *pl); |
47 | void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, | 47 | void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, |
48 | u32 clock, u16 max_voltage, u16 *voltage); | 48 | u32 clock, u16 max_voltage, u16 *voltage); |
49 | void btc_get_max_clock_from_voltage_dependency_table(struct radeon_clock_voltage_dependency_table *table, | ||
50 | u32 *max_clock); | ||
49 | void btc_apply_voltage_delta_rules(struct radeon_device *rdev, | 51 | void btc_apply_voltage_delta_rules(struct radeon_device *rdev, |
50 | u16 max_vddc, u16 max_vddci, | 52 | u16 max_vddc, u16 max_vddci, |
51 | u16 *vddc, u16 *vddci); | 53 | u16 *vddc, u16 *vddci); |
diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index f5c8c0445a94..11a55e9dad7f 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/firmware.h> | 24 | #include <linux/firmware.h> |
25 | #include "drmP.h" | 25 | #include "drmP.h" |
26 | #include "radeon.h" | 26 | #include "radeon.h" |
27 | #include "radeon_asic.h" | ||
27 | #include "radeon_ucode.h" | 28 | #include "radeon_ucode.h" |
28 | #include "cikd.h" | 29 | #include "cikd.h" |
29 | #include "r600_dpm.h" | 30 | #include "r600_dpm.h" |
diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c index c77dad1a4576..4e8432d07f15 100644 --- a/drivers/gpu/drm/radeon/cik_sdma.c +++ b/drivers/gpu/drm/radeon/cik_sdma.c | |||
@@ -611,16 +611,19 @@ int cik_sdma_ring_test(struct radeon_device *rdev, | |||
611 | { | 611 | { |
612 | unsigned i; | 612 | unsigned i; |
613 | int r; | 613 | int r; |
614 | void __iomem *ptr = (void *)rdev->vram_scratch.ptr; | 614 | unsigned index; |
615 | u32 tmp; | 615 | u32 tmp; |
616 | u64 gpu_addr; | ||
616 | 617 | ||
617 | if (!ptr) { | 618 | if (ring->idx == R600_RING_TYPE_DMA_INDEX) |
618 | DRM_ERROR("invalid vram scratch pointer\n"); | 619 | index = R600_WB_DMA_RING_TEST_OFFSET; |
619 | return -EINVAL; | 620 | else |
620 | } | 621 | index = CAYMAN_WB_DMA1_RING_TEST_OFFSET; |
622 | |||
623 | gpu_addr = rdev->wb.gpu_addr + index; | ||
621 | 624 | ||
622 | tmp = 0xCAFEDEAD; | 625 | tmp = 0xCAFEDEAD; |
623 | writel(tmp, ptr); | 626 | rdev->wb.wb[index/4] = cpu_to_le32(tmp); |
624 | 627 | ||
625 | r = radeon_ring_lock(rdev, ring, 5); | 628 | r = radeon_ring_lock(rdev, ring, 5); |
626 | if (r) { | 629 | if (r) { |
@@ -628,14 +631,14 @@ int cik_sdma_ring_test(struct radeon_device *rdev, | |||
628 | return r; | 631 | return r; |
629 | } | 632 | } |
630 | radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); | 633 | radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); |
631 | radeon_ring_write(ring, rdev->vram_scratch.gpu_addr & 0xfffffffc); | 634 | radeon_ring_write(ring, lower_32_bits(gpu_addr)); |
632 | radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr)); | 635 | radeon_ring_write(ring, upper_32_bits(gpu_addr)); |
633 | radeon_ring_write(ring, 1); /* number of DWs to follow */ | 636 | radeon_ring_write(ring, 1); /* number of DWs to follow */ |
634 | radeon_ring_write(ring, 0xDEADBEEF); | 637 | radeon_ring_write(ring, 0xDEADBEEF); |
635 | radeon_ring_unlock_commit(rdev, ring, false); | 638 | radeon_ring_unlock_commit(rdev, ring, false); |
636 | 639 | ||
637 | for (i = 0; i < rdev->usec_timeout; i++) { | 640 | for (i = 0; i < rdev->usec_timeout; i++) { |
638 | tmp = readl(ptr); | 641 | tmp = le32_to_cpu(rdev->wb.wb[index/4]); |
639 | if (tmp == 0xDEADBEEF) | 642 | if (tmp == 0xDEADBEEF) |
640 | break; | 643 | break; |
641 | DRM_UDELAY(1); | 644 | DRM_UDELAY(1); |
diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c index 47d31e915758..9aad0327e4d1 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.c +++ b/drivers/gpu/drm/radeon/cypress_dpm.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include "drmP.h" | 25 | #include "drmP.h" |
26 | #include "radeon.h" | 26 | #include "radeon.h" |
27 | #include "radeon_asic.h" | ||
27 | #include "evergreend.h" | 28 | #include "evergreend.h" |
28 | #include "r600_dpm.h" | 29 | #include "r600_dpm.h" |
29 | #include "cypress_dpm.h" | 30 | #include "cypress_dpm.h" |
diff --git a/drivers/gpu/drm/radeon/dce3_1_afmt.c b/drivers/gpu/drm/radeon/dce3_1_afmt.c index 950af153f30e..2fe8cfc966d9 100644 --- a/drivers/gpu/drm/radeon/dce3_1_afmt.c +++ b/drivers/gpu/drm/radeon/dce3_1_afmt.c | |||
@@ -32,7 +32,7 @@ static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder) | |||
32 | struct drm_connector *connector; | 32 | struct drm_connector *connector; |
33 | struct radeon_connector *radeon_connector = NULL; | 33 | struct radeon_connector *radeon_connector = NULL; |
34 | u32 tmp; | 34 | u32 tmp; |
35 | u8 *sadb; | 35 | u8 *sadb = NULL; |
36 | int sad_count; | 36 | int sad_count; |
37 | 37 | ||
38 | list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) { | 38 | list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) { |
@@ -49,8 +49,8 @@ static void dce3_2_afmt_write_speaker_allocation(struct drm_encoder *encoder) | |||
49 | 49 | ||
50 | sad_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb); | 50 | sad_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb); |
51 | if (sad_count < 0) { | 51 | if (sad_count < 0) { |
52 | DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); | 52 | DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); |
53 | return; | 53 | sad_count = 0; |
54 | } | 54 | } |
55 | 55 | ||
56 | /* program the speaker allocation */ | 56 | /* program the speaker allocation */ |
diff --git a/drivers/gpu/drm/radeon/dce6_afmt.c b/drivers/gpu/drm/radeon/dce6_afmt.c index c0bbf68dbc27..f312edf4d50e 100644 --- a/drivers/gpu/drm/radeon/dce6_afmt.c +++ b/drivers/gpu/drm/radeon/dce6_afmt.c | |||
@@ -155,7 +155,7 @@ void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder) | |||
155 | struct drm_connector *connector; | 155 | struct drm_connector *connector; |
156 | struct radeon_connector *radeon_connector = NULL; | 156 | struct radeon_connector *radeon_connector = NULL; |
157 | u32 offset, tmp; | 157 | u32 offset, tmp; |
158 | u8 *sadb; | 158 | u8 *sadb = NULL; |
159 | int sad_count; | 159 | int sad_count; |
160 | 160 | ||
161 | if (!dig || !dig->afmt || !dig->afmt->pin) | 161 | if (!dig || !dig->afmt || !dig->afmt->pin) |
@@ -176,9 +176,9 @@ void dce6_afmt_write_speaker_allocation(struct drm_encoder *encoder) | |||
176 | } | 176 | } |
177 | 177 | ||
178 | sad_count = drm_edid_to_speaker_allocation(radeon_connector_edid(connector), &sadb); | 178 | sad_count = drm_edid_to_speaker_allocation(radeon_connector_edid(connector), &sadb); |
179 | if (sad_count <= 0) { | 179 | if (sad_count < 0) { |
180 | DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); | 180 | DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); |
181 | return; | 181 | sad_count = 0; |
182 | } | 182 | } |
183 | 183 | ||
184 | /* program the speaker allocation */ | 184 | /* program the speaker allocation */ |
diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c index 2514d659b1ba..53abd9b17a50 100644 --- a/drivers/gpu/drm/radeon/evergreen_hdmi.c +++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c | |||
@@ -133,7 +133,7 @@ static void dce4_afmt_write_speaker_allocation(struct drm_encoder *encoder) | |||
133 | struct drm_connector *connector; | 133 | struct drm_connector *connector; |
134 | struct radeon_connector *radeon_connector = NULL; | 134 | struct radeon_connector *radeon_connector = NULL; |
135 | u32 tmp; | 135 | u32 tmp; |
136 | u8 *sadb; | 136 | u8 *sadb = NULL; |
137 | int sad_count; | 137 | int sad_count; |
138 | 138 | ||
139 | list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) { | 139 | list_for_each_entry(connector, &encoder->dev->mode_config.connector_list, head) { |
@@ -149,9 +149,9 @@ static void dce4_afmt_write_speaker_allocation(struct drm_encoder *encoder) | |||
149 | } | 149 | } |
150 | 150 | ||
151 | sad_count = drm_edid_to_speaker_allocation(radeon_connector_edid(connector), &sadb); | 151 | sad_count = drm_edid_to_speaker_allocation(radeon_connector_edid(connector), &sadb); |
152 | if (sad_count <= 0) { | 152 | if (sad_count < 0) { |
153 | DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); | 153 | DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); |
154 | return; | 154 | sad_count = 0; |
155 | } | 155 | } |
156 | 156 | ||
157 | /* program the speaker allocation */ | 157 | /* program the speaker allocation */ |
diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 715b181c6243..6d2f16cf2c1c 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | #include "drmP.h" | 24 | #include "drmP.h" |
25 | #include "radeon.h" | 25 | #include "radeon.h" |
26 | #include "radeon_asic.h" | ||
26 | #include "nid.h" | 27 | #include "nid.h" |
27 | #include "r600_dpm.h" | 28 | #include "r600_dpm.h" |
28 | #include "ni_dpm.h" | 29 | #include "ni_dpm.h" |
diff --git a/drivers/gpu/drm/radeon/r600_dma.c b/drivers/gpu/drm/radeon/r600_dma.c index 100189ec5fa8..aabc343b9a8f 100644 --- a/drivers/gpu/drm/radeon/r600_dma.c +++ b/drivers/gpu/drm/radeon/r600_dma.c | |||
@@ -232,16 +232,19 @@ int r600_dma_ring_test(struct radeon_device *rdev, | |||
232 | { | 232 | { |
233 | unsigned i; | 233 | unsigned i; |
234 | int r; | 234 | int r; |
235 | void __iomem *ptr = (void *)rdev->vram_scratch.ptr; | 235 | unsigned index; |
236 | u32 tmp; | 236 | u32 tmp; |
237 | u64 gpu_addr; | ||
237 | 238 | ||
238 | if (!ptr) { | 239 | if (ring->idx == R600_RING_TYPE_DMA_INDEX) |
239 | DRM_ERROR("invalid vram scratch pointer\n"); | 240 | index = R600_WB_DMA_RING_TEST_OFFSET; |
240 | return -EINVAL; | 241 | else |
241 | } | 242 | index = CAYMAN_WB_DMA1_RING_TEST_OFFSET; |
243 | |||
244 | gpu_addr = rdev->wb.gpu_addr + index; | ||
242 | 245 | ||
243 | tmp = 0xCAFEDEAD; | 246 | tmp = 0xCAFEDEAD; |
244 | writel(tmp, ptr); | 247 | rdev->wb.wb[index/4] = cpu_to_le32(tmp); |
245 | 248 | ||
246 | r = radeon_ring_lock(rdev, ring, 4); | 249 | r = radeon_ring_lock(rdev, ring, 4); |
247 | if (r) { | 250 | if (r) { |
@@ -249,13 +252,13 @@ int r600_dma_ring_test(struct radeon_device *rdev, | |||
249 | return r; | 252 | return r; |
250 | } | 253 | } |
251 | radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1)); | 254 | radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1)); |
252 | radeon_ring_write(ring, rdev->vram_scratch.gpu_addr & 0xfffffffc); | 255 | radeon_ring_write(ring, lower_32_bits(gpu_addr)); |
253 | radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xff); | 256 | radeon_ring_write(ring, upper_32_bits(gpu_addr) & 0xff); |
254 | radeon_ring_write(ring, 0xDEADBEEF); | 257 | radeon_ring_write(ring, 0xDEADBEEF); |
255 | radeon_ring_unlock_commit(rdev, ring, false); | 258 | radeon_ring_unlock_commit(rdev, ring, false); |
256 | 259 | ||
257 | for (i = 0; i < rdev->usec_timeout; i++) { | 260 | for (i = 0; i < rdev->usec_timeout; i++) { |
258 | tmp = readl(ptr); | 261 | tmp = le32_to_cpu(rdev->wb.wb[index/4]); |
259 | if (tmp == 0xDEADBEEF) | 262 | if (tmp == 0xDEADBEEF) |
260 | break; | 263 | break; |
261 | DRM_UDELAY(1); | 264 | DRM_UDELAY(1); |
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index 9c61b74ef441..f6309bd23e01 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include "drmP.h" | 25 | #include "drmP.h" |
26 | #include "radeon.h" | 26 | #include "radeon.h" |
27 | #include "radeon_asic.h" | ||
27 | #include "r600d.h" | 28 | #include "r600d.h" |
28 | #include "r600_dpm.h" | 29 | #include "r600_dpm.h" |
29 | #include "atom.h" | 30 | #include "atom.h" |
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index f7c4b226a284..a9717b3fbf1b 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h | |||
@@ -1133,6 +1133,8 @@ struct radeon_wb { | |||
1133 | #define R600_WB_EVENT_OFFSET 3072 | 1133 | #define R600_WB_EVENT_OFFSET 3072 |
1134 | #define CIK_WB_CP1_WPTR_OFFSET 3328 | 1134 | #define CIK_WB_CP1_WPTR_OFFSET 3328 |
1135 | #define CIK_WB_CP2_WPTR_OFFSET 3584 | 1135 | #define CIK_WB_CP2_WPTR_OFFSET 3584 |
1136 | #define R600_WB_DMA_RING_TEST_OFFSET 3588 | ||
1137 | #define CAYMAN_WB_DMA1_RING_TEST_OFFSET 3592 | ||
1136 | 1138 | ||
1137 | /** | 1139 | /** |
1138 | * struct radeon_pm - power management datas | 1140 | * struct radeon_pm - power management datas |
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index f41cc1538e48..ea2676954dde 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c | |||
@@ -1130,7 +1130,7 @@ static void radeon_check_arguments(struct radeon_device *rdev) | |||
1130 | if (radeon_vm_block_size == -1) { | 1130 | if (radeon_vm_block_size == -1) { |
1131 | 1131 | ||
1132 | /* Total bits covered by PD + PTs */ | 1132 | /* Total bits covered by PD + PTs */ |
1133 | unsigned bits = ilog2(radeon_vm_size) + 17; | 1133 | unsigned bits = ilog2(radeon_vm_size) + 18; |
1134 | 1134 | ||
1135 | /* Make sure the PD is 4K in size up to 8GB address space. | 1135 | /* Make sure the PD is 4K in size up to 8GB address space. |
1136 | Above that split equal between PD and PTs */ | 1136 | Above that split equal between PD and PTs */ |
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c index 02f7710de470..9031f4b69824 100644 --- a/drivers/gpu/drm/radeon/rs780_dpm.c +++ b/drivers/gpu/drm/radeon/rs780_dpm.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include "drmP.h" | 25 | #include "drmP.h" |
26 | #include "radeon.h" | 26 | #include "radeon.h" |
27 | #include "radeon_asic.h" | ||
27 | #include "rs780d.h" | 28 | #include "rs780d.h" |
28 | #include "r600_dpm.h" | 29 | #include "r600_dpm.h" |
29 | #include "rs780_dpm.h" | 30 | #include "rs780_dpm.h" |
diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c index e7045b085715..6a5c233361e9 100644 --- a/drivers/gpu/drm/radeon/rv6xx_dpm.c +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include "drmP.h" | 25 | #include "drmP.h" |
26 | #include "radeon.h" | 26 | #include "radeon.h" |
27 | #include "radeon_asic.h" | ||
27 | #include "rv6xxd.h" | 28 | #include "rv6xxd.h" |
28 | #include "r600_dpm.h" | 29 | #include "r600_dpm.h" |
29 | #include "rv6xx_dpm.h" | 30 | #include "rv6xx_dpm.h" |
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index 3c76e1dcdf04..755a8f96fe46 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include "drmP.h" | 25 | #include "drmP.h" |
26 | #include "radeon.h" | 26 | #include "radeon.h" |
27 | #include "radeon_asic.h" | ||
27 | #include "rv770d.h" | 28 | #include "rv770d.h" |
28 | #include "r600_dpm.h" | 29 | #include "r600_dpm.h" |
29 | #include "rv770_dpm.h" | 30 | #include "rv770_dpm.h" |
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 9e4d5d7d348f..a53c2e79d9cb 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | #include "drmP.h" | 24 | #include "drmP.h" |
25 | #include "radeon.h" | 25 | #include "radeon.h" |
26 | #include "radeon_asic.h" | ||
26 | #include "sid.h" | 27 | #include "sid.h" |
27 | #include "r600_dpm.h" | 28 | #include "r600_dpm.h" |
28 | #include "si_dpm.h" | 29 | #include "si_dpm.h" |
@@ -2916,6 +2917,7 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, | |||
2916 | bool disable_sclk_switching = false; | 2917 | bool disable_sclk_switching = false; |
2917 | u32 mclk, sclk; | 2918 | u32 mclk, sclk; |
2918 | u16 vddc, vddci; | 2919 | u16 vddc, vddci; |
2920 | u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; | ||
2919 | int i; | 2921 | int i; |
2920 | 2922 | ||
2921 | if ((rdev->pm.dpm.new_active_crtc_count > 1) || | 2923 | if ((rdev->pm.dpm.new_active_crtc_count > 1) || |
@@ -2949,6 +2951,29 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, | |||
2949 | } | 2951 | } |
2950 | } | 2952 | } |
2951 | 2953 | ||
2954 | /* limit clocks to max supported clocks based on voltage dependency tables */ | ||
2955 | btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, | ||
2956 | &max_sclk_vddc); | ||
2957 | btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, | ||
2958 | &max_mclk_vddci); | ||
2959 | btc_get_max_clock_from_voltage_dependency_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, | ||
2960 | &max_mclk_vddc); | ||
2961 | |||
2962 | for (i = 0; i < ps->performance_level_count; i++) { | ||
2963 | if (max_sclk_vddc) { | ||
2964 | if (ps->performance_levels[i].sclk > max_sclk_vddc) | ||
2965 | ps->performance_levels[i].sclk = max_sclk_vddc; | ||
2966 | } | ||
2967 | if (max_mclk_vddci) { | ||
2968 | if (ps->performance_levels[i].mclk > max_mclk_vddci) | ||
2969 | ps->performance_levels[i].mclk = max_mclk_vddci; | ||
2970 | } | ||
2971 | if (max_mclk_vddc) { | ||
2972 | if (ps->performance_levels[i].mclk > max_mclk_vddc) | ||
2973 | ps->performance_levels[i].mclk = max_mclk_vddc; | ||
2974 | } | ||
2975 | } | ||
2976 | |||
2952 | /* XXX validate the min clocks required for display */ | 2977 | /* XXX validate the min clocks required for display */ |
2953 | 2978 | ||
2954 | if (disable_mclk_switching) { | 2979 | if (disable_mclk_switching) { |
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index 3f0e8d7b8dbe..1f8a8833e1be 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | #include "drmP.h" | 24 | #include "drmP.h" |
25 | #include "radeon.h" | 25 | #include "radeon.h" |
26 | #include "radeon_asic.h" | ||
26 | #include "sumod.h" | 27 | #include "sumod.h" |
27 | #include "r600_dpm.h" | 28 | #include "r600_dpm.h" |
28 | #include "cypress_dpm.h" | 29 | #include "cypress_dpm.h" |
diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index 57f780053b3e..b4ec5c4e7969 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | #include "drmP.h" | 24 | #include "drmP.h" |
25 | #include "radeon.h" | 25 | #include "radeon.h" |
26 | #include "radeon_asic.h" | ||
26 | #include "trinityd.h" | 27 | #include "trinityd.h" |
27 | #include "r600_dpm.h" | 28 | #include "r600_dpm.h" |
28 | #include "trinity_dpm.h" | 29 | #include "trinity_dpm.h" |
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 8f5cec67c47d..d395b0bef73b 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c | |||
@@ -709,6 +709,7 @@ out: | |||
709 | 709 | ||
710 | static int ttm_mem_evict_first(struct ttm_bo_device *bdev, | 710 | static int ttm_mem_evict_first(struct ttm_bo_device *bdev, |
711 | uint32_t mem_type, | 711 | uint32_t mem_type, |
712 | const struct ttm_place *place, | ||
712 | bool interruptible, | 713 | bool interruptible, |
713 | bool no_wait_gpu) | 714 | bool no_wait_gpu) |
714 | { | 715 | { |
@@ -720,8 +721,21 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, | |||
720 | spin_lock(&glob->lru_lock); | 721 | spin_lock(&glob->lru_lock); |
721 | list_for_each_entry(bo, &man->lru, lru) { | 722 | list_for_each_entry(bo, &man->lru, lru) { |
722 | ret = __ttm_bo_reserve(bo, false, true, false, NULL); | 723 | ret = __ttm_bo_reserve(bo, false, true, false, NULL); |
723 | if (!ret) | 724 | if (!ret) { |
725 | if (place && (place->fpfn || place->lpfn)) { | ||
726 | /* Don't evict this BO if it's outside of the | ||
727 | * requested placement range | ||
728 | */ | ||
729 | if (place->fpfn >= (bo->mem.start + bo->mem.size) || | ||
730 | (place->lpfn && place->lpfn <= bo->mem.start)) { | ||
731 | __ttm_bo_unreserve(bo); | ||
732 | ret = -EBUSY; | ||
733 | continue; | ||
734 | } | ||
735 | } | ||
736 | |||
724 | break; | 737 | break; |
738 | } | ||
725 | } | 739 | } |
726 | 740 | ||
727 | if (ret) { | 741 | if (ret) { |
@@ -782,7 +796,7 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, | |||
782 | return ret; | 796 | return ret; |
783 | if (mem->mm_node) | 797 | if (mem->mm_node) |
784 | break; | 798 | break; |
785 | ret = ttm_mem_evict_first(bdev, mem_type, | 799 | ret = ttm_mem_evict_first(bdev, mem_type, place, |
786 | interruptible, no_wait_gpu); | 800 | interruptible, no_wait_gpu); |
787 | if (unlikely(ret != 0)) | 801 | if (unlikely(ret != 0)) |
788 | return ret; | 802 | return ret; |
@@ -994,9 +1008,9 @@ static bool ttm_bo_mem_compat(struct ttm_placement *placement, | |||
994 | 1008 | ||
995 | for (i = 0; i < placement->num_placement; i++) { | 1009 | for (i = 0; i < placement->num_placement; i++) { |
996 | const struct ttm_place *heap = &placement->placement[i]; | 1010 | const struct ttm_place *heap = &placement->placement[i]; |
997 | if (mem->mm_node && heap->lpfn != 0 && | 1011 | if (mem->mm_node && |
998 | (mem->start < heap->fpfn || | 1012 | (mem->start < heap->fpfn || |
999 | mem->start + mem->num_pages > heap->lpfn)) | 1013 | (heap->lpfn != 0 && (mem->start + mem->num_pages) > heap->lpfn))) |
1000 | continue; | 1014 | continue; |
1001 | 1015 | ||
1002 | *new_flags = heap->flags; | 1016 | *new_flags = heap->flags; |
@@ -1007,9 +1021,9 @@ static bool ttm_bo_mem_compat(struct ttm_placement *placement, | |||
1007 | 1021 | ||
1008 | for (i = 0; i < placement->num_busy_placement; i++) { | 1022 | for (i = 0; i < placement->num_busy_placement; i++) { |
1009 | const struct ttm_place *heap = &placement->busy_placement[i]; | 1023 | const struct ttm_place *heap = &placement->busy_placement[i]; |
1010 | if (mem->mm_node && heap->lpfn != 0 && | 1024 | if (mem->mm_node && |
1011 | (mem->start < heap->fpfn || | 1025 | (mem->start < heap->fpfn || |
1012 | mem->start + mem->num_pages > heap->lpfn)) | 1026 | (heap->lpfn != 0 && (mem->start + mem->num_pages) > heap->lpfn))) |
1013 | continue; | 1027 | continue; |
1014 | 1028 | ||
1015 | *new_flags = heap->flags; | 1029 | *new_flags = heap->flags; |
@@ -1233,7 +1247,7 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, | |||
1233 | spin_lock(&glob->lru_lock); | 1247 | spin_lock(&glob->lru_lock); |
1234 | while (!list_empty(&man->lru)) { | 1248 | while (!list_empty(&man->lru)) { |
1235 | spin_unlock(&glob->lru_lock); | 1249 | spin_unlock(&glob->lru_lock); |
1236 | ret = ttm_mem_evict_first(bdev, mem_type, false, false); | 1250 | ret = ttm_mem_evict_first(bdev, mem_type, NULL, false, false); |
1237 | if (ret) { | 1251 | if (ret) { |
1238 | if (allow_errors) { | 1252 | if (allow_errors) { |
1239 | return ret; | 1253 | return ret; |
diff --git a/drivers/hwmon/menf21bmc_hwmon.c b/drivers/hwmon/menf21bmc_hwmon.c index c92229d321c9..afc6b58eaa62 100644 --- a/drivers/hwmon/menf21bmc_hwmon.c +++ b/drivers/hwmon/menf21bmc_hwmon.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/jiffies.h> | 21 | #include <linux/jiffies.h> |
22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | #include <linux/i2c.h> | 23 | #include <linux/i2c.h> |
24 | #include <linux/err.h> | ||
24 | 25 | ||
25 | #define DRV_NAME "menf21bmc_hwmon" | 26 | #define DRV_NAME "menf21bmc_hwmon" |
26 | 27 | ||
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 0bea5776bcbc..3effa931fce2 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c | |||
@@ -2185,7 +2185,7 @@ isert_put_response(struct iscsi_conn *conn, struct iscsi_cmd *cmd) | |||
2185 | isert_cmd->tx_desc.num_sge = 2; | 2185 | isert_cmd->tx_desc.num_sge = 2; |
2186 | } | 2186 | } |
2187 | 2187 | ||
2188 | isert_init_send_wr(isert_conn, isert_cmd, send_wr, true); | 2188 | isert_init_send_wr(isert_conn, isert_cmd, send_wr, false); |
2189 | 2189 | ||
2190 | pr_debug("Posting SCSI Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n"); | 2190 | pr_debug("Posting SCSI Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n"); |
2191 | 2191 | ||
@@ -2871,7 +2871,7 @@ isert_put_datain(struct iscsi_conn *conn, struct iscsi_cmd *cmd) | |||
2871 | &isert_cmd->tx_desc.iscsi_header); | 2871 | &isert_cmd->tx_desc.iscsi_header); |
2872 | isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc); | 2872 | isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc); |
2873 | isert_init_send_wr(isert_conn, isert_cmd, | 2873 | isert_init_send_wr(isert_conn, isert_cmd, |
2874 | &isert_cmd->tx_desc.send_wr, true); | 2874 | &isert_cmd->tx_desc.send_wr, false); |
2875 | isert_cmd->rdma_wr.s_send_wr.next = &isert_cmd->tx_desc.send_wr; | 2875 | isert_cmd->rdma_wr.s_send_wr.next = &isert_cmd->tx_desc.send_wr; |
2876 | wr->send_wr_num += 1; | 2876 | wr->send_wr_num += 1; |
2877 | } | 2877 | } |
@@ -3140,7 +3140,7 @@ isert_accept_np(struct iscsi_np *np, struct iscsi_conn *conn) | |||
3140 | 3140 | ||
3141 | accept_wait: | 3141 | accept_wait: |
3142 | ret = down_interruptible(&isert_np->np_sem); | 3142 | ret = down_interruptible(&isert_np->np_sem); |
3143 | if (max_accept > 5) | 3143 | if (ret || max_accept > 5) |
3144 | return -ENODEV; | 3144 | return -ENODEV; |
3145 | 3145 | ||
3146 | spin_lock_bh(&np->np_thread_lock); | 3146 | spin_lock_bh(&np->np_thread_lock); |
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index aa29198fca3e..7440c58b8e6f 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c | |||
@@ -9,26 +9,21 @@ | |||
9 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/module.h> | 12 | #include <linux/ctype.h> |
13 | #include <linux/kernel.h> | 13 | #include <linux/device.h> |
14 | #include <linux/err.h> | ||
14 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/kernel.h> | ||
17 | #include <linux/leds.h> | ||
15 | #include <linux/list.h> | 18 | #include <linux/list.h> |
19 | #include <linux/module.h> | ||
20 | #include <linux/slab.h> | ||
16 | #include <linux/spinlock.h> | 21 | #include <linux/spinlock.h> |
17 | #include <linux/device.h> | ||
18 | #include <linux/timer.h> | 22 | #include <linux/timer.h> |
19 | #include <linux/err.h> | ||
20 | #include <linux/ctype.h> | ||
21 | #include <linux/leds.h> | ||
22 | #include "leds.h" | 23 | #include "leds.h" |
23 | 24 | ||
24 | static struct class *leds_class; | 25 | static struct class *leds_class; |
25 | 26 | ||
26 | static void led_update_brightness(struct led_classdev *led_cdev) | ||
27 | { | ||
28 | if (led_cdev->brightness_get) | ||
29 | led_cdev->brightness = led_cdev->brightness_get(led_cdev); | ||
30 | } | ||
31 | |||
32 | static ssize_t brightness_show(struct device *dev, | 27 | static ssize_t brightness_show(struct device *dev, |
33 | struct device_attribute *attr, char *buf) | 28 | struct device_attribute *attr, char *buf) |
34 | { | 29 | { |
@@ -59,14 +54,14 @@ static ssize_t brightness_store(struct device *dev, | |||
59 | } | 54 | } |
60 | static DEVICE_ATTR_RW(brightness); | 55 | static DEVICE_ATTR_RW(brightness); |
61 | 56 | ||
62 | static ssize_t led_max_brightness_show(struct device *dev, | 57 | static ssize_t max_brightness_show(struct device *dev, |
63 | struct device_attribute *attr, char *buf) | 58 | struct device_attribute *attr, char *buf) |
64 | { | 59 | { |
65 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 60 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
66 | 61 | ||
67 | return sprintf(buf, "%u\n", led_cdev->max_brightness); | 62 | return sprintf(buf, "%u\n", led_cdev->max_brightness); |
68 | } | 63 | } |
69 | static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL); | 64 | static DEVICE_ATTR_RO(max_brightness); |
70 | 65 | ||
71 | #ifdef CONFIG_LEDS_TRIGGERS | 66 | #ifdef CONFIG_LEDS_TRIGGERS |
72 | static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); | 67 | static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); |
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 71b40d3bf776..aaa8eba9099f 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c | |||
@@ -12,10 +12,11 @@ | |||
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/leds.h> | ||
15 | #include <linux/list.h> | 16 | #include <linux/list.h> |
16 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/mutex.h> | ||
17 | #include <linux/rwsem.h> | 19 | #include <linux/rwsem.h> |
18 | #include <linux/leds.h> | ||
19 | #include "leds.h" | 20 | #include "leds.h" |
20 | 21 | ||
21 | DECLARE_RWSEM(leds_list_lock); | 22 | DECLARE_RWSEM(leds_list_lock); |
@@ -126,3 +127,19 @@ void led_set_brightness(struct led_classdev *led_cdev, | |||
126 | __led_set_brightness(led_cdev, brightness); | 127 | __led_set_brightness(led_cdev, brightness); |
127 | } | 128 | } |
128 | EXPORT_SYMBOL(led_set_brightness); | 129 | EXPORT_SYMBOL(led_set_brightness); |
130 | |||
131 | int led_update_brightness(struct led_classdev *led_cdev) | ||
132 | { | ||
133 | int ret = 0; | ||
134 | |||
135 | if (led_cdev->brightness_get) { | ||
136 | ret = led_cdev->brightness_get(led_cdev); | ||
137 | if (ret >= 0) { | ||
138 | led_cdev->brightness = ret; | ||
139 | return 0; | ||
140 | } | ||
141 | } | ||
142 | |||
143 | return ret; | ||
144 | } | ||
145 | EXPORT_SYMBOL(led_update_brightness); | ||
diff --git a/drivers/leds/leds-gpio-register.c b/drivers/leds/leds-gpio-register.c index 1c4ed5510f35..75717ba68ae0 100644 --- a/drivers/leds/leds-gpio-register.c +++ b/drivers/leds/leds-gpio-register.c | |||
@@ -7,9 +7,9 @@ | |||
7 | * Free Software Foundation. | 7 | * Free Software Foundation. |
8 | */ | 8 | */ |
9 | #include <linux/err.h> | 9 | #include <linux/err.h> |
10 | #include <linux/leds.h> | ||
10 | #include <linux/platform_device.h> | 11 | #include <linux/platform_device.h> |
11 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
12 | #include <linux/leds.h> | ||
13 | 13 | ||
14 | /** | 14 | /** |
15 | * gpio_led_register_device - register a gpio-led device | 15 | * gpio_led_register_device - register a gpio-led device |
@@ -28,6 +28,9 @@ struct platform_device *__init gpio_led_register_device( | |||
28 | struct platform_device *ret; | 28 | struct platform_device *ret; |
29 | struct gpio_led_platform_data _pdata = *pdata; | 29 | struct gpio_led_platform_data _pdata = *pdata; |
30 | 30 | ||
31 | if (!pdata->num_leds) | ||
32 | return ERR_PTR(-EINVAL); | ||
33 | |||
31 | _pdata.leds = kmemdup(pdata->leds, | 34 | _pdata.leds = kmemdup(pdata->leds, |
32 | pdata->num_leds * sizeof(*pdata->leds), GFP_KERNEL); | 35 | pdata->num_leds * sizeof(*pdata->leds), GFP_KERNEL); |
33 | if (!_pdata.leds) | 36 | if (!_pdata.leds) |
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 57ff20fecf57..b4518c8751c8 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c | |||
@@ -10,17 +10,17 @@ | |||
10 | * published by the Free Software Foundation. | 10 | * published by the Free Software Foundation. |
11 | * | 11 | * |
12 | */ | 12 | */ |
13 | #include <linux/kernel.h> | 13 | #include <linux/err.h> |
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/gpio.h> | 14 | #include <linux/gpio.h> |
15 | #include <linux/kernel.h> | ||
16 | #include <linux/leds.h> | 16 | #include <linux/leds.h> |
17 | #include <linux/module.h> | ||
17 | #include <linux/of.h> | 18 | #include <linux/of.h> |
18 | #include <linux/of_platform.h> | ||
19 | #include <linux/of_gpio.h> | 19 | #include <linux/of_gpio.h> |
20 | #include <linux/of_platform.h> | ||
21 | #include <linux/platform_device.h> | ||
20 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
21 | #include <linux/workqueue.h> | 23 | #include <linux/workqueue.h> |
22 | #include <linux/module.h> | ||
23 | #include <linux/err.h> | ||
24 | 24 | ||
25 | struct gpio_led_data { | 25 | struct gpio_led_data { |
26 | struct led_classdev cdev; | 26 | struct led_classdev cdev; |
@@ -36,7 +36,7 @@ struct gpio_led_data { | |||
36 | 36 | ||
37 | static void gpio_led_work(struct work_struct *work) | 37 | static void gpio_led_work(struct work_struct *work) |
38 | { | 38 | { |
39 | struct gpio_led_data *led_dat = | 39 | struct gpio_led_data *led_dat = |
40 | container_of(work, struct gpio_led_data, work); | 40 | container_of(work, struct gpio_led_data, work); |
41 | 41 | ||
42 | if (led_dat->blinking) { | 42 | if (led_dat->blinking) { |
@@ -235,14 +235,12 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) | |||
235 | } | 235 | } |
236 | #endif /* CONFIG_OF_GPIO */ | 236 | #endif /* CONFIG_OF_GPIO */ |
237 | 237 | ||
238 | |||
239 | static int gpio_led_probe(struct platform_device *pdev) | 238 | static int gpio_led_probe(struct platform_device *pdev) |
240 | { | 239 | { |
241 | struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); | 240 | struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
242 | struct gpio_leds_priv *priv; | 241 | struct gpio_leds_priv *priv; |
243 | int i, ret = 0; | 242 | int i, ret = 0; |
244 | 243 | ||
245 | |||
246 | if (pdata && pdata->num_leds) { | 244 | if (pdata && pdata->num_leds) { |
247 | priv = devm_kzalloc(&pdev->dev, | 245 | priv = devm_kzalloc(&pdev->dev, |
248 | sizeof_gpio_leds_priv(pdata->num_leds), | 246 | sizeof_gpio_leds_priv(pdata->num_leds), |
diff --git a/drivers/leds/leds-lp3944.c b/drivers/leds/leds-lp3944.c index 8e1abdcd4c9d..53144fb96167 100644 --- a/drivers/leds/leds-lp3944.c +++ b/drivers/leds/leds-lp3944.c | |||
@@ -335,7 +335,8 @@ static int lp3944_configure(struct i2c_client *client, | |||
335 | } | 335 | } |
336 | 336 | ||
337 | /* to expose the default value to userspace */ | 337 | /* to expose the default value to userspace */ |
338 | led->ldev.brightness = led->status; | 338 | led->ldev.brightness = |
339 | (enum led_brightness) led->status; | ||
339 | 340 | ||
340 | /* Set the default led status */ | 341 | /* Set the default led status */ |
341 | err = lp3944_led_set(led, led->status); | 342 | err = lp3944_led_set(led, led->status); |
diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c index 35812e3a37f2..c86c41826476 100644 --- a/drivers/leds/trigger/ledtrig-gpio.c +++ b/drivers/leds/trigger/ledtrig-gpio.c | |||
@@ -48,7 +48,7 @@ static void gpio_trig_work(struct work_struct *work) | |||
48 | if (!gpio_data->gpio) | 48 | if (!gpio_data->gpio) |
49 | return; | 49 | return; |
50 | 50 | ||
51 | tmp = gpio_get_value(gpio_data->gpio); | 51 | tmp = gpio_get_value_cansleep(gpio_data->gpio); |
52 | if (gpio_data->inverted) | 52 | if (gpio_data->inverted) |
53 | tmp = !tmp; | 53 | tmp = !tmp; |
54 | 54 | ||
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 6d184dbcaca8..94ed7cefb14d 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile | |||
@@ -1,3 +1,7 @@ | |||
1 | # Generic MAILBOX API | ||
2 | |||
3 | obj-$(CONFIG_MAILBOX) += mailbox.o | ||
4 | |||
1 | obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o | 5 | obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o |
2 | 6 | ||
3 | obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o | 7 | obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o |
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c new file mode 100644 index 000000000000..afcb430508ec --- /dev/null +++ b/drivers/mailbox/mailbox.c | |||
@@ -0,0 +1,465 @@ | |||
1 | /* | ||
2 | * Mailbox: Common code for Mailbox controllers and users | ||
3 | * | ||
4 | * Copyright (C) 2013-2014 Linaro Ltd. | ||
5 | * Author: Jassi Brar <jassisinghbrar@gmail.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/spinlock.h> | ||
14 | #include <linux/mutex.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/bitops.h> | ||
21 | #include <linux/mailbox_client.h> | ||
22 | #include <linux/mailbox_controller.h> | ||
23 | |||
24 | #define TXDONE_BY_IRQ BIT(0) /* controller has remote RTR irq */ | ||
25 | #define TXDONE_BY_POLL BIT(1) /* controller can read status of last TX */ | ||
26 | #define TXDONE_BY_ACK BIT(2) /* S/W ACK recevied by Client ticks the TX */ | ||
27 | |||
28 | static LIST_HEAD(mbox_cons); | ||
29 | static DEFINE_MUTEX(con_mutex); | ||
30 | |||
31 | static int add_to_rbuf(struct mbox_chan *chan, void *mssg) | ||
32 | { | ||
33 | int idx; | ||
34 | unsigned long flags; | ||
35 | |||
36 | spin_lock_irqsave(&chan->lock, flags); | ||
37 | |||
38 | /* See if there is any space left */ | ||
39 | if (chan->msg_count == MBOX_TX_QUEUE_LEN) { | ||
40 | spin_unlock_irqrestore(&chan->lock, flags); | ||
41 | return -ENOBUFS; | ||
42 | } | ||
43 | |||
44 | idx = chan->msg_free; | ||
45 | chan->msg_data[idx] = mssg; | ||
46 | chan->msg_count++; | ||
47 | |||
48 | if (idx == MBOX_TX_QUEUE_LEN - 1) | ||
49 | chan->msg_free = 0; | ||
50 | else | ||
51 | chan->msg_free++; | ||
52 | |||
53 | spin_unlock_irqrestore(&chan->lock, flags); | ||
54 | |||
55 | return idx; | ||
56 | } | ||
57 | |||
58 | static void msg_submit(struct mbox_chan *chan) | ||
59 | { | ||
60 | unsigned count, idx; | ||
61 | unsigned long flags; | ||
62 | void *data; | ||
63 | int err; | ||
64 | |||
65 | spin_lock_irqsave(&chan->lock, flags); | ||
66 | |||
67 | if (!chan->msg_count || chan->active_req) | ||
68 | goto exit; | ||
69 | |||
70 | count = chan->msg_count; | ||
71 | idx = chan->msg_free; | ||
72 | if (idx >= count) | ||
73 | idx -= count; | ||
74 | else | ||
75 | idx += MBOX_TX_QUEUE_LEN - count; | ||
76 | |||
77 | data = chan->msg_data[idx]; | ||
78 | |||
79 | /* Try to submit a message to the MBOX controller */ | ||
80 | err = chan->mbox->ops->send_data(chan, data); | ||
81 | if (!err) { | ||
82 | chan->active_req = data; | ||
83 | chan->msg_count--; | ||
84 | } | ||
85 | exit: | ||
86 | spin_unlock_irqrestore(&chan->lock, flags); | ||
87 | } | ||
88 | |||
89 | static void tx_tick(struct mbox_chan *chan, int r) | ||
90 | { | ||
91 | unsigned long flags; | ||
92 | void *mssg; | ||
93 | |||
94 | spin_lock_irqsave(&chan->lock, flags); | ||
95 | mssg = chan->active_req; | ||
96 | chan->active_req = NULL; | ||
97 | spin_unlock_irqrestore(&chan->lock, flags); | ||
98 | |||
99 | /* Submit next message */ | ||
100 | msg_submit(chan); | ||
101 | |||
102 | /* Notify the client */ | ||
103 | if (mssg && chan->cl->tx_done) | ||
104 | chan->cl->tx_done(chan->cl, mssg, r); | ||
105 | |||
106 | if (chan->cl->tx_block) | ||
107 | complete(&chan->tx_complete); | ||
108 | } | ||
109 | |||
110 | static void poll_txdone(unsigned long data) | ||
111 | { | ||
112 | struct mbox_controller *mbox = (struct mbox_controller *)data; | ||
113 | bool txdone, resched = false; | ||
114 | int i; | ||
115 | |||
116 | for (i = 0; i < mbox->num_chans; i++) { | ||
117 | struct mbox_chan *chan = &mbox->chans[i]; | ||
118 | |||
119 | if (chan->active_req && chan->cl) { | ||
120 | resched = true; | ||
121 | txdone = chan->mbox->ops->last_tx_done(chan); | ||
122 | if (txdone) | ||
123 | tx_tick(chan, 0); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | if (resched) | ||
128 | mod_timer(&mbox->poll, jiffies + | ||
129 | msecs_to_jiffies(mbox->txpoll_period)); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * mbox_chan_received_data - A way for controller driver to push data | ||
134 | * received from remote to the upper layer. | ||
135 | * @chan: Pointer to the mailbox channel on which RX happened. | ||
136 | * @mssg: Client specific message typecasted as void * | ||
137 | * | ||
138 | * After startup and before shutdown any data received on the chan | ||
139 | * is passed on to the API via atomic mbox_chan_received_data(). | ||
140 | * The controller should ACK the RX only after this call returns. | ||
141 | */ | ||
142 | void mbox_chan_received_data(struct mbox_chan *chan, void *mssg) | ||
143 | { | ||
144 | /* No buffering the received data */ | ||
145 | if (chan->cl->rx_callback) | ||
146 | chan->cl->rx_callback(chan->cl, mssg); | ||
147 | } | ||
148 | EXPORT_SYMBOL_GPL(mbox_chan_received_data); | ||
149 | |||
150 | /** | ||
151 | * mbox_chan_txdone - A way for controller driver to notify the | ||
152 | * framework that the last TX has completed. | ||
153 | * @chan: Pointer to the mailbox chan on which TX happened. | ||
154 | * @r: Status of last TX - OK or ERROR | ||
155 | * | ||
156 | * The controller that has IRQ for TX ACK calls this atomic API | ||
157 | * to tick the TX state machine. It works only if txdone_irq | ||
158 | * is set by the controller. | ||
159 | */ | ||
160 | void mbox_chan_txdone(struct mbox_chan *chan, int r) | ||
161 | { | ||
162 | if (unlikely(!(chan->txdone_method & TXDONE_BY_IRQ))) { | ||
163 | dev_err(chan->mbox->dev, | ||
164 | "Controller can't run the TX ticker\n"); | ||
165 | return; | ||
166 | } | ||
167 | |||
168 | tx_tick(chan, r); | ||
169 | } | ||
170 | EXPORT_SYMBOL_GPL(mbox_chan_txdone); | ||
171 | |||
172 | /** | ||
173 | * mbox_client_txdone - The way for a client to run the TX state machine. | ||
174 | * @chan: Mailbox channel assigned to this client. | ||
175 | * @r: Success status of last transmission. | ||
176 | * | ||
177 | * The client/protocol had received some 'ACK' packet and it notifies | ||
178 | * the API that the last packet was sent successfully. This only works | ||
179 | * if the controller can't sense TX-Done. | ||
180 | */ | ||
181 | void mbox_client_txdone(struct mbox_chan *chan, int r) | ||
182 | { | ||
183 | if (unlikely(!(chan->txdone_method & TXDONE_BY_ACK))) { | ||
184 | dev_err(chan->mbox->dev, "Client can't run the TX ticker\n"); | ||
185 | return; | ||
186 | } | ||
187 | |||
188 | tx_tick(chan, r); | ||
189 | } | ||
190 | EXPORT_SYMBOL_GPL(mbox_client_txdone); | ||
191 | |||
192 | /** | ||
193 | * mbox_client_peek_data - A way for client driver to pull data | ||
194 | * received from remote by the controller. | ||
195 | * @chan: Mailbox channel assigned to this client. | ||
196 | * | ||
197 | * A poke to controller driver for any received data. | ||
198 | * The data is actually passed onto client via the | ||
199 | * mbox_chan_received_data() | ||
200 | * The call can be made from atomic context, so the controller's | ||
201 | * implementation of peek_data() must not sleep. | ||
202 | * | ||
203 | * Return: True, if controller has, and is going to push after this, | ||
204 | * some data. | ||
205 | * False, if controller doesn't have any data to be read. | ||
206 | */ | ||
207 | bool mbox_client_peek_data(struct mbox_chan *chan) | ||
208 | { | ||
209 | if (chan->mbox->ops->peek_data) | ||
210 | return chan->mbox->ops->peek_data(chan); | ||
211 | |||
212 | return false; | ||
213 | } | ||
214 | EXPORT_SYMBOL_GPL(mbox_client_peek_data); | ||
215 | |||
216 | /** | ||
217 | * mbox_send_message - For client to submit a message to be | ||
218 | * sent to the remote. | ||
219 | * @chan: Mailbox channel assigned to this client. | ||
220 | * @mssg: Client specific message typecasted. | ||
221 | * | ||
222 | * For client to submit data to the controller destined for a remote | ||
223 | * processor. If the client had set 'tx_block', the call will return | ||
224 | * either when the remote receives the data or when 'tx_tout' millisecs | ||
225 | * run out. | ||
226 | * In non-blocking mode, the requests are buffered by the API and a | ||
227 | * non-negative token is returned for each queued request. If the request | ||
228 | * is not queued, a negative token is returned. Upon failure or successful | ||
229 | * TX, the API calls 'tx_done' from atomic context, from which the client | ||
230 | * could submit yet another request. | ||
231 | * The pointer to message should be preserved until it is sent | ||
232 | * over the chan, i.e, tx_done() is made. | ||
233 | * This function could be called from atomic context as it simply | ||
234 | * queues the data and returns a token against the request. | ||
235 | * | ||
236 | * Return: Non-negative integer for successful submission (non-blocking mode) | ||
237 | * or transmission over chan (blocking mode). | ||
238 | * Negative value denotes failure. | ||
239 | */ | ||
240 | int mbox_send_message(struct mbox_chan *chan, void *mssg) | ||
241 | { | ||
242 | int t; | ||
243 | |||
244 | if (!chan || !chan->cl) | ||
245 | return -EINVAL; | ||
246 | |||
247 | t = add_to_rbuf(chan, mssg); | ||
248 | if (t < 0) { | ||
249 | dev_err(chan->mbox->dev, "Try increasing MBOX_TX_QUEUE_LEN\n"); | ||
250 | return t; | ||
251 | } | ||
252 | |||
253 | msg_submit(chan); | ||
254 | |||
255 | if (chan->txdone_method == TXDONE_BY_POLL) | ||
256 | poll_txdone((unsigned long)chan->mbox); | ||
257 | |||
258 | if (chan->cl->tx_block && chan->active_req) { | ||
259 | unsigned long wait; | ||
260 | int ret; | ||
261 | |||
262 | if (!chan->cl->tx_tout) /* wait forever */ | ||
263 | wait = msecs_to_jiffies(3600000); | ||
264 | else | ||
265 | wait = msecs_to_jiffies(chan->cl->tx_tout); | ||
266 | |||
267 | ret = wait_for_completion_timeout(&chan->tx_complete, wait); | ||
268 | if (ret == 0) { | ||
269 | t = -EIO; | ||
270 | tx_tick(chan, -EIO); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | return t; | ||
275 | } | ||
276 | EXPORT_SYMBOL_GPL(mbox_send_message); | ||
277 | |||
278 | /** | ||
279 | * mbox_request_channel - Request a mailbox channel. | ||
280 | * @cl: Identity of the client requesting the channel. | ||
281 | * @index: Index of mailbox specifier in 'mboxes' property. | ||
282 | * | ||
283 | * The Client specifies its requirements and capabilities while asking for | ||
284 | * a mailbox channel. It can't be called from atomic context. | ||
285 | * The channel is exclusively allocated and can't be used by another | ||
286 | * client before the owner calls mbox_free_channel. | ||
287 | * After assignment, any packet received on this channel will be | ||
288 | * handed over to the client via the 'rx_callback'. | ||
289 | * The framework holds reference to the client, so the mbox_client | ||
290 | * structure shouldn't be modified until the mbox_free_channel returns. | ||
291 | * | ||
292 | * Return: Pointer to the channel assigned to the client if successful. | ||
293 | * ERR_PTR for request failure. | ||
294 | */ | ||
295 | struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index) | ||
296 | { | ||
297 | struct device *dev = cl->dev; | ||
298 | struct mbox_controller *mbox; | ||
299 | struct of_phandle_args spec; | ||
300 | struct mbox_chan *chan; | ||
301 | unsigned long flags; | ||
302 | int ret; | ||
303 | |||
304 | if (!dev || !dev->of_node) { | ||
305 | pr_debug("%s: No owner device node\n", __func__); | ||
306 | return ERR_PTR(-ENODEV); | ||
307 | } | ||
308 | |||
309 | mutex_lock(&con_mutex); | ||
310 | |||
311 | if (of_parse_phandle_with_args(dev->of_node, "mboxes", | ||
312 | "#mbox-cells", index, &spec)) { | ||
313 | dev_dbg(dev, "%s: can't parse \"mboxes\" property\n", __func__); | ||
314 | mutex_unlock(&con_mutex); | ||
315 | return ERR_PTR(-ENODEV); | ||
316 | } | ||
317 | |||
318 | chan = NULL; | ||
319 | list_for_each_entry(mbox, &mbox_cons, node) | ||
320 | if (mbox->dev->of_node == spec.np) { | ||
321 | chan = mbox->of_xlate(mbox, &spec); | ||
322 | break; | ||
323 | } | ||
324 | |||
325 | of_node_put(spec.np); | ||
326 | |||
327 | if (!chan || chan->cl || !try_module_get(mbox->dev->driver->owner)) { | ||
328 | dev_dbg(dev, "%s: mailbox not free\n", __func__); | ||
329 | mutex_unlock(&con_mutex); | ||
330 | return ERR_PTR(-EBUSY); | ||
331 | } | ||
332 | |||
333 | spin_lock_irqsave(&chan->lock, flags); | ||
334 | chan->msg_free = 0; | ||
335 | chan->msg_count = 0; | ||
336 | chan->active_req = NULL; | ||
337 | chan->cl = cl; | ||
338 | init_completion(&chan->tx_complete); | ||
339 | |||
340 | if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone) | ||
341 | chan->txdone_method |= TXDONE_BY_ACK; | ||
342 | |||
343 | spin_unlock_irqrestore(&chan->lock, flags); | ||
344 | |||
345 | ret = chan->mbox->ops->startup(chan); | ||
346 | if (ret) { | ||
347 | dev_err(dev, "Unable to startup the chan (%d)\n", ret); | ||
348 | mbox_free_channel(chan); | ||
349 | chan = ERR_PTR(ret); | ||
350 | } | ||
351 | |||
352 | mutex_unlock(&con_mutex); | ||
353 | return chan; | ||
354 | } | ||
355 | EXPORT_SYMBOL_GPL(mbox_request_channel); | ||
356 | |||
357 | /** | ||
358 | * mbox_free_channel - The client relinquishes control of a mailbox | ||
359 | * channel by this call. | ||
360 | * @chan: The mailbox channel to be freed. | ||
361 | */ | ||
362 | void mbox_free_channel(struct mbox_chan *chan) | ||
363 | { | ||
364 | unsigned long flags; | ||
365 | |||
366 | if (!chan || !chan->cl) | ||
367 | return; | ||
368 | |||
369 | chan->mbox->ops->shutdown(chan); | ||
370 | |||
371 | /* The queued TX requests are simply aborted, no callbacks are made */ | ||
372 | spin_lock_irqsave(&chan->lock, flags); | ||
373 | chan->cl = NULL; | ||
374 | chan->active_req = NULL; | ||
375 | if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK)) | ||
376 | chan->txdone_method = TXDONE_BY_POLL; | ||
377 | |||
378 | module_put(chan->mbox->dev->driver->owner); | ||
379 | spin_unlock_irqrestore(&chan->lock, flags); | ||
380 | } | ||
381 | EXPORT_SYMBOL_GPL(mbox_free_channel); | ||
382 | |||
383 | static struct mbox_chan * | ||
384 | of_mbox_index_xlate(struct mbox_controller *mbox, | ||
385 | const struct of_phandle_args *sp) | ||
386 | { | ||
387 | int ind = sp->args[0]; | ||
388 | |||
389 | if (ind >= mbox->num_chans) | ||
390 | return NULL; | ||
391 | |||
392 | return &mbox->chans[ind]; | ||
393 | } | ||
394 | |||
395 | /** | ||
396 | * mbox_controller_register - Register the mailbox controller | ||
397 | * @mbox: Pointer to the mailbox controller. | ||
398 | * | ||
399 | * The controller driver registers its communication channels | ||
400 | */ | ||
401 | int mbox_controller_register(struct mbox_controller *mbox) | ||
402 | { | ||
403 | int i, txdone; | ||
404 | |||
405 | /* Sanity check */ | ||
406 | if (!mbox || !mbox->dev || !mbox->ops || !mbox->num_chans) | ||
407 | return -EINVAL; | ||
408 | |||
409 | if (mbox->txdone_irq) | ||
410 | txdone = TXDONE_BY_IRQ; | ||
411 | else if (mbox->txdone_poll) | ||
412 | txdone = TXDONE_BY_POLL; | ||
413 | else /* It has to be ACK then */ | ||
414 | txdone = TXDONE_BY_ACK; | ||
415 | |||
416 | if (txdone == TXDONE_BY_POLL) { | ||
417 | mbox->poll.function = &poll_txdone; | ||
418 | mbox->poll.data = (unsigned long)mbox; | ||
419 | init_timer(&mbox->poll); | ||
420 | } | ||
421 | |||
422 | for (i = 0; i < mbox->num_chans; i++) { | ||
423 | struct mbox_chan *chan = &mbox->chans[i]; | ||
424 | |||
425 | chan->cl = NULL; | ||
426 | chan->mbox = mbox; | ||
427 | chan->txdone_method = txdone; | ||
428 | spin_lock_init(&chan->lock); | ||
429 | } | ||
430 | |||
431 | if (!mbox->of_xlate) | ||
432 | mbox->of_xlate = of_mbox_index_xlate; | ||
433 | |||
434 | mutex_lock(&con_mutex); | ||
435 | list_add_tail(&mbox->node, &mbox_cons); | ||
436 | mutex_unlock(&con_mutex); | ||
437 | |||
438 | return 0; | ||
439 | } | ||
440 | EXPORT_SYMBOL_GPL(mbox_controller_register); | ||
441 | |||
442 | /** | ||
443 | * mbox_controller_unregister - Unregister the mailbox controller | ||
444 | * @mbox: Pointer to the mailbox controller. | ||
445 | */ | ||
446 | void mbox_controller_unregister(struct mbox_controller *mbox) | ||
447 | { | ||
448 | int i; | ||
449 | |||
450 | if (!mbox) | ||
451 | return; | ||
452 | |||
453 | mutex_lock(&con_mutex); | ||
454 | |||
455 | list_del(&mbox->node); | ||
456 | |||
457 | for (i = 0; i < mbox->num_chans; i++) | ||
458 | mbox_free_channel(&mbox->chans[i]); | ||
459 | |||
460 | if (mbox->txdone_poll) | ||
461 | del_timer_sync(&mbox->poll); | ||
462 | |||
463 | mutex_unlock(&con_mutex); | ||
464 | } | ||
465 | EXPORT_SYMBOL_GPL(mbox_controller_unregister); | ||
diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c index d873cbae2fbb..f3755e0aa935 100644 --- a/drivers/mailbox/pl320-ipc.c +++ b/drivers/mailbox/pl320-ipc.c | |||
@@ -26,7 +26,7 @@ | |||
26 | #include <linux/device.h> | 26 | #include <linux/device.h> |
27 | #include <linux/amba/bus.h> | 27 | #include <linux/amba/bus.h> |
28 | 28 | ||
29 | #include <linux/mailbox.h> | 29 | #include <linux/pl320-ipc.h> |
30 | 30 | ||
31 | #define IPCMxSOURCE(m) ((m) * 0x40) | 31 | #define IPCMxSOURCE(m) ((m) * 0x40) |
32 | #define IPCMxDSET(m) (((m) * 0x40) + 0x004) | 32 | #define IPCMxDSET(m) (((m) * 0x40) + 0x004) |
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index a9f9c46e5022..63fc63911295 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c | |||
@@ -397,6 +397,7 @@ static int pcie_pme_suspend(struct pcie_device *srv) | |||
397 | struct pcie_pme_service_data *data = get_service_data(srv); | 397 | struct pcie_pme_service_data *data = get_service_data(srv); |
398 | struct pci_dev *port = srv->port; | 398 | struct pci_dev *port = srv->port; |
399 | bool wakeup; | 399 | bool wakeup; |
400 | int ret; | ||
400 | 401 | ||
401 | if (device_may_wakeup(&port->dev)) { | 402 | if (device_may_wakeup(&port->dev)) { |
402 | wakeup = true; | 403 | wakeup = true; |
@@ -407,9 +408,10 @@ static int pcie_pme_suspend(struct pcie_device *srv) | |||
407 | } | 408 | } |
408 | spin_lock_irq(&data->lock); | 409 | spin_lock_irq(&data->lock); |
409 | if (wakeup) { | 410 | if (wakeup) { |
410 | enable_irq_wake(srv->irq); | 411 | ret = enable_irq_wake(srv->irq); |
411 | data->suspend_level = PME_SUSPEND_WAKEUP; | 412 | data->suspend_level = PME_SUSPEND_WAKEUP; |
412 | } else { | 413 | } |
414 | if (!wakeup || ret) { | ||
413 | struct pci_dev *port = srv->port; | 415 | struct pci_dev *port = srv->port; |
414 | 416 | ||
415 | pcie_pme_interrupt_enable(port, false); | 417 | pcie_pme_interrupt_enable(port, false); |
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 3611806c9cfd..3cb36693343a 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c | |||
@@ -100,11 +100,11 @@ static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd) | |||
100 | /* Disable SDRAM0 accesses */ | 100 | /* Disable SDRAM0 accesses */ |
101 | "1: str %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | 101 | "1: str %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" |
102 | /* Power down SDRAM0 */ | 102 | /* Power down SDRAM0 */ |
103 | " str %4, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | 103 | " str %4, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" |
104 | /* Disable SDRAM1 accesses */ | 104 | /* Disable SDRAM1 accesses */ |
105 | " strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | 105 | " strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" |
106 | /* Power down SDRAM1 */ | 106 | /* Power down SDRAM1 */ |
107 | " strne %4, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" | 107 | " strne %4, [%1, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" |
108 | /* Reset CPU */ | 108 | /* Reset CPU */ |
109 | " str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t" | 109 | " str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t" |
110 | 110 | ||
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index b800783800a3..ef2dd2e4754b 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig | |||
@@ -83,6 +83,7 @@ config PWM_BFIN | |||
83 | config PWM_CLPS711X | 83 | config PWM_CLPS711X |
84 | tristate "CLPS711X PWM support" | 84 | tristate "CLPS711X PWM support" |
85 | depends on ARCH_CLPS711X || COMPILE_TEST | 85 | depends on ARCH_CLPS711X || COMPILE_TEST |
86 | depends on HAS_IOMEM | ||
86 | help | 87 | help |
87 | Generic PWM framework driver for Cirrus Logic CLPS711X. | 88 | Generic PWM framework driver for Cirrus Logic CLPS711X. |
88 | 89 | ||
@@ -101,6 +102,7 @@ config PWM_EP93XX | |||
101 | config PWM_FSL_FTM | 102 | config PWM_FSL_FTM |
102 | tristate "Freescale FlexTimer Module (FTM) PWM support" | 103 | tristate "Freescale FlexTimer Module (FTM) PWM support" |
103 | depends on OF | 104 | depends on OF |
105 | select REGMAP_MMIO | ||
104 | help | 106 | help |
105 | Generic FTM PWM framework driver for Freescale VF610 and | 107 | Generic FTM PWM framework driver for Freescale VF610 and |
106 | Layerscape LS-1 SoCs. | 108 | Layerscape LS-1 SoCs. |
@@ -149,7 +151,7 @@ config PWM_LPC32XX | |||
149 | 151 | ||
150 | config PWM_LPSS | 152 | config PWM_LPSS |
151 | tristate "Intel LPSS PWM support" | 153 | tristate "Intel LPSS PWM support" |
152 | depends on ACPI | 154 | depends on X86 |
153 | help | 155 | help |
154 | Generic PWM framework driver for Intel Low Power Subsystem PWM | 156 | Generic PWM framework driver for Intel Low Power Subsystem PWM |
155 | controller. | 157 | controller. |
@@ -157,6 +159,24 @@ config PWM_LPSS | |||
157 | To compile this driver as a module, choose M here: the module | 159 | To compile this driver as a module, choose M here: the module |
158 | will be called pwm-lpss. | 160 | will be called pwm-lpss. |
159 | 161 | ||
162 | config PWM_LPSS_PCI | ||
163 | tristate "Intel LPSS PWM PCI driver" | ||
164 | depends on PWM_LPSS && PCI | ||
165 | help | ||
166 | The PCI driver for Intel Low Power Subsystem PWM controller. | ||
167 | |||
168 | To compile this driver as a module, choose M here: the module | ||
169 | will be called pwm-lpss-pci. | ||
170 | |||
171 | config PWM_LPSS_PLATFORM | ||
172 | tristate "Intel LPSS PWM platform driver" | ||
173 | depends on PWM_LPSS && ACPI | ||
174 | help | ||
175 | The platform driver for Intel Low Power Subsystem PWM controller. | ||
176 | |||
177 | To compile this driver as a module, choose M here: the module | ||
178 | will be called pwm-lpss-platform. | ||
179 | |||
160 | config PWM_MXS | 180 | config PWM_MXS |
161 | tristate "Freescale MXS PWM support" | 181 | tristate "Freescale MXS PWM support" |
162 | depends on ARCH_MXS && OF | 182 | depends on ARCH_MXS && OF |
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index f8c577d41091..c458606c3755 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile | |||
@@ -13,6 +13,8 @@ obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o | |||
13 | obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o | 13 | obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o |
14 | obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o | 14 | obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o |
15 | obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o | 15 | obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o |
16 | obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o | ||
17 | obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o | ||
16 | obj-$(CONFIG_PWM_MXS) += pwm-mxs.o | 18 | obj-$(CONFIG_PWM_MXS) += pwm-mxs.o |
17 | obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o | 19 | obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o |
18 | obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o | 20 | obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o |
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index d2c35920ff08..966497d10c6e 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c | |||
@@ -236,7 +236,7 @@ int pwmchip_add(struct pwm_chip *chip) | |||
236 | int ret; | 236 | int ret; |
237 | 237 | ||
238 | if (!chip || !chip->dev || !chip->ops || !chip->ops->config || | 238 | if (!chip || !chip->dev || !chip->ops || !chip->ops->config || |
239 | !chip->ops->enable || !chip->ops->disable) | 239 | !chip->ops->enable || !chip->ops->disable || !chip->npwm) |
240 | return -EINVAL; | 240 | return -EINVAL; |
241 | 241 | ||
242 | mutex_lock(&pwm_lock); | 242 | mutex_lock(&pwm_lock); |
@@ -602,12 +602,9 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) | |||
602 | struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER); | 602 | struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER); |
603 | const char *dev_id = dev ? dev_name(dev) : NULL; | 603 | const char *dev_id = dev ? dev_name(dev) : NULL; |
604 | struct pwm_chip *chip = NULL; | 604 | struct pwm_chip *chip = NULL; |
605 | unsigned int index = 0; | ||
606 | unsigned int best = 0; | 605 | unsigned int best = 0; |
607 | struct pwm_lookup *p; | 606 | struct pwm_lookup *p, *chosen = NULL; |
608 | unsigned int match; | 607 | unsigned int match; |
609 | unsigned int period; | ||
610 | enum pwm_polarity polarity; | ||
611 | 608 | ||
612 | /* look up via DT first */ | 609 | /* look up via DT first */ |
613 | if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) | 610 | if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) |
@@ -653,10 +650,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) | |||
653 | } | 650 | } |
654 | 651 | ||
655 | if (match > best) { | 652 | if (match > best) { |
656 | chip = pwmchip_find_by_name(p->provider); | 653 | chosen = p; |
657 | index = p->index; | ||
658 | period = p->period; | ||
659 | polarity = p->polarity; | ||
660 | 654 | ||
661 | if (match != 3) | 655 | if (match != 3) |
662 | best = match; | 656 | best = match; |
@@ -665,17 +659,22 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) | |||
665 | } | 659 | } |
666 | } | 660 | } |
667 | 661 | ||
668 | mutex_unlock(&pwm_lookup_lock); | 662 | if (!chosen) |
663 | goto out; | ||
669 | 664 | ||
670 | if (chip) | 665 | chip = pwmchip_find_by_name(chosen->provider); |
671 | pwm = pwm_request_from_chip(chip, index, con_id ?: dev_id); | 666 | if (!chip) |
672 | if (IS_ERR(pwm)) | 667 | goto out; |
673 | return pwm; | ||
674 | 668 | ||
675 | pwm_set_period(pwm, period); | 669 | pwm = pwm_request_from_chip(chip, chosen->index, con_id ?: dev_id); |
676 | pwm_set_polarity(pwm, polarity); | 670 | if (IS_ERR(pwm)) |
671 | goto out; | ||
677 | 672 | ||
673 | pwm_set_period(pwm, chosen->period); | ||
674 | pwm_set_polarity(pwm, chosen->polarity); | ||
678 | 675 | ||
676 | out: | ||
677 | mutex_unlock(&pwm_lookup_lock); | ||
679 | return pwm; | 678 | return pwm; |
680 | } | 679 | } |
681 | EXPORT_SYMBOL_GPL(pwm_get); | 680 | EXPORT_SYMBOL_GPL(pwm_get); |
diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 6e700a541ca3..d3c22de9ee47 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c | |||
@@ -102,7 +102,7 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
102 | int duty_ns, int period_ns) | 102 | int duty_ns, int period_ns) |
103 | { | 103 | { |
104 | struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); | 104 | struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); |
105 | unsigned long clk_rate, prd, dty; | 105 | unsigned long prd, dty; |
106 | unsigned long long div; | 106 | unsigned long long div; |
107 | unsigned int pres = 0; | 107 | unsigned int pres = 0; |
108 | u32 val; | 108 | u32 val; |
@@ -113,20 +113,18 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
113 | return -EBUSY; | 113 | return -EBUSY; |
114 | } | 114 | } |
115 | 115 | ||
116 | clk_rate = clk_get_rate(atmel_pwm->clk); | 116 | /* Calculate the period cycles and prescale value */ |
117 | div = clk_rate; | 117 | div = (unsigned long long)clk_get_rate(atmel_pwm->clk) * period_ns; |
118 | do_div(div, NSEC_PER_SEC); | ||
118 | 119 | ||
119 | /* Calculate the period cycles */ | ||
120 | while (div > PWM_MAX_PRD) { | 120 | while (div > PWM_MAX_PRD) { |
121 | div = clk_rate / (1 << pres); | 121 | div >>= 1; |
122 | div = div * period_ns; | 122 | pres++; |
123 | /* 1/Hz = 100000000 ns */ | 123 | } |
124 | do_div(div, 1000000000); | 124 | |
125 | 125 | if (pres > PRD_MAX_PRES) { | |
126 | if (pres++ > PRD_MAX_PRES) { | 126 | dev_err(chip->dev, "pres exceeds the maximum value\n"); |
127 | dev_err(chip->dev, "pres exceeds the maximum value\n"); | 127 | return -EINVAL; |
128 | return -EINVAL; | ||
129 | } | ||
130 | } | 128 | } |
131 | 129 | ||
132 | /* Calculate the duty cycles */ | 130 | /* Calculate the duty cycles */ |
diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index a18bc8fea385..0f2cc7ef7784 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c | |||
@@ -18,14 +18,14 @@ | |||
18 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
20 | #include <linux/pwm.h> | 20 | #include <linux/pwm.h> |
21 | #include <linux/regmap.h> | ||
21 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
22 | 23 | ||
23 | #define FTM_SC 0x00 | 24 | #define FTM_SC 0x00 |
24 | #define FTM_SC_CLK_MASK 0x3 | 25 | #define FTM_SC_CLK_MASK_SHIFT 3 |
25 | #define FTM_SC_CLK_SHIFT 3 | 26 | #define FTM_SC_CLK_MASK (3 << FTM_SC_CLK_MASK_SHIFT) |
26 | #define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_SHIFT) | 27 | #define FTM_SC_CLK(c) (((c) + 1) << FTM_SC_CLK_MASK_SHIFT) |
27 | #define FTM_SC_PS_MASK 0x7 | 28 | #define FTM_SC_PS_MASK 0x7 |
28 | #define FTM_SC_PS_SHIFT 0 | ||
29 | 29 | ||
30 | #define FTM_CNT 0x04 | 30 | #define FTM_CNT 0x04 |
31 | #define FTM_MOD 0x08 | 31 | #define FTM_MOD 0x08 |
@@ -83,7 +83,7 @@ struct fsl_pwm_chip { | |||
83 | unsigned int cnt_select; | 83 | unsigned int cnt_select; |
84 | unsigned int clk_ps; | 84 | unsigned int clk_ps; |
85 | 85 | ||
86 | void __iomem *base; | 86 | struct regmap *regmap; |
87 | 87 | ||
88 | int period_ns; | 88 | int period_ns; |
89 | 89 | ||
@@ -219,10 +219,11 @@ static unsigned long fsl_pwm_calculate_duty(struct fsl_pwm_chip *fpc, | |||
219 | unsigned long period_ns, | 219 | unsigned long period_ns, |
220 | unsigned long duty_ns) | 220 | unsigned long duty_ns) |
221 | { | 221 | { |
222 | unsigned long long val, duty; | 222 | unsigned long long duty; |
223 | u32 val; | ||
223 | 224 | ||
224 | val = readl(fpc->base + FTM_MOD); | 225 | regmap_read(fpc->regmap, FTM_MOD, &val); |
225 | duty = duty_ns * (val + 1); | 226 | duty = (unsigned long long)duty_ns * (val + 1); |
226 | do_div(duty, period_ns); | 227 | do_div(duty, period_ns); |
227 | 228 | ||
228 | return (unsigned long)duty; | 229 | return (unsigned long)duty; |
@@ -232,7 +233,7 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
232 | int duty_ns, int period_ns) | 233 | int duty_ns, int period_ns) |
233 | { | 234 | { |
234 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | 235 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
235 | u32 val, period, duty; | 236 | u32 period, duty; |
236 | 237 | ||
237 | mutex_lock(&fpc->lock); | 238 | mutex_lock(&fpc->lock); |
238 | 239 | ||
@@ -257,11 +258,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
257 | return -EINVAL; | 258 | return -EINVAL; |
258 | } | 259 | } |
259 | 260 | ||
260 | val = readl(fpc->base + FTM_SC); | 261 | regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_PS_MASK, |
261 | val &= ~(FTM_SC_PS_MASK << FTM_SC_PS_SHIFT); | 262 | fpc->clk_ps); |
262 | val |= fpc->clk_ps; | 263 | regmap_write(fpc->regmap, FTM_MOD, period - 1); |
263 | writel(val, fpc->base + FTM_SC); | ||
264 | writel(period - 1, fpc->base + FTM_MOD); | ||
265 | 264 | ||
266 | fpc->period_ns = period_ns; | 265 | fpc->period_ns = period_ns; |
267 | } | 266 | } |
@@ -270,8 +269,9 @@ static int fsl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
270 | 269 | ||
271 | duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); | 270 | duty = fsl_pwm_calculate_duty(fpc, period_ns, duty_ns); |
272 | 271 | ||
273 | writel(FTM_CSC_MSB | FTM_CSC_ELSB, fpc->base + FTM_CSC(pwm->hwpwm)); | 272 | regmap_write(fpc->regmap, FTM_CSC(pwm->hwpwm), |
274 | writel(duty, fpc->base + FTM_CV(pwm->hwpwm)); | 273 | FTM_CSC_MSB | FTM_CSC_ELSB); |
274 | regmap_write(fpc->regmap, FTM_CV(pwm->hwpwm), duty); | ||
275 | 275 | ||
276 | return 0; | 276 | return 0; |
277 | } | 277 | } |
@@ -283,31 +283,28 @@ static int fsl_pwm_set_polarity(struct pwm_chip *chip, | |||
283 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | 283 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
284 | u32 val; | 284 | u32 val; |
285 | 285 | ||
286 | val = readl(fpc->base + FTM_POL); | 286 | regmap_read(fpc->regmap, FTM_POL, &val); |
287 | 287 | ||
288 | if (polarity == PWM_POLARITY_INVERSED) | 288 | if (polarity == PWM_POLARITY_INVERSED) |
289 | val |= BIT(pwm->hwpwm); | 289 | val |= BIT(pwm->hwpwm); |
290 | else | 290 | else |
291 | val &= ~BIT(pwm->hwpwm); | 291 | val &= ~BIT(pwm->hwpwm); |
292 | 292 | ||
293 | writel(val, fpc->base + FTM_POL); | 293 | regmap_write(fpc->regmap, FTM_POL, val); |
294 | 294 | ||
295 | return 0; | 295 | return 0; |
296 | } | 296 | } |
297 | 297 | ||
298 | static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) | 298 | static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) |
299 | { | 299 | { |
300 | u32 val; | ||
301 | int ret; | 300 | int ret; |
302 | 301 | ||
303 | if (fpc->use_count != 0) | 302 | if (fpc->use_count != 0) |
304 | return 0; | 303 | return 0; |
305 | 304 | ||
306 | /* select counter clock source */ | 305 | /* select counter clock source */ |
307 | val = readl(fpc->base + FTM_SC); | 306 | regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, |
308 | val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); | 307 | FTM_SC_CLK(fpc->cnt_select)); |
309 | val |= FTM_SC_CLK(fpc->cnt_select); | ||
310 | writel(val, fpc->base + FTM_SC); | ||
311 | 308 | ||
312 | ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); | 309 | ret = clk_prepare_enable(fpc->clk[fpc->cnt_select]); |
313 | if (ret) | 310 | if (ret) |
@@ -327,13 +324,10 @@ static int fsl_counter_clock_enable(struct fsl_pwm_chip *fpc) | |||
327 | static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | 324 | static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
328 | { | 325 | { |
329 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); | 326 | struct fsl_pwm_chip *fpc = to_fsl_chip(chip); |
330 | u32 val; | ||
331 | int ret; | 327 | int ret; |
332 | 328 | ||
333 | mutex_lock(&fpc->lock); | 329 | mutex_lock(&fpc->lock); |
334 | val = readl(fpc->base + FTM_OUTMASK); | 330 | regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), 0); |
335 | val &= ~BIT(pwm->hwpwm); | ||
336 | writel(val, fpc->base + FTM_OUTMASK); | ||
337 | 331 | ||
338 | ret = fsl_counter_clock_enable(fpc); | 332 | ret = fsl_counter_clock_enable(fpc); |
339 | mutex_unlock(&fpc->lock); | 333 | mutex_unlock(&fpc->lock); |
@@ -343,8 +337,6 @@ static int fsl_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
343 | 337 | ||
344 | static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) | 338 | static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) |
345 | { | 339 | { |
346 | u32 val; | ||
347 | |||
348 | /* | 340 | /* |
349 | * already disabled, do nothing | 341 | * already disabled, do nothing |
350 | */ | 342 | */ |
@@ -356,9 +348,7 @@ static void fsl_counter_clock_disable(struct fsl_pwm_chip *fpc) | |||
356 | return; | 348 | return; |
357 | 349 | ||
358 | /* no users left, disable PWM counter clock */ | 350 | /* no users left, disable PWM counter clock */ |
359 | val = readl(fpc->base + FTM_SC); | 351 | regmap_update_bits(fpc->regmap, FTM_SC, FTM_SC_CLK_MASK, 0); |
360 | val &= ~(FTM_SC_CLK_MASK << FTM_SC_CLK_SHIFT); | ||
361 | writel(val, fpc->base + FTM_SC); | ||
362 | 352 | ||
363 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); | 353 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_CNTEN]); |
364 | clk_disable_unprepare(fpc->clk[fpc->cnt_select]); | 354 | clk_disable_unprepare(fpc->clk[fpc->cnt_select]); |
@@ -370,14 +360,12 @@ static void fsl_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
370 | u32 val; | 360 | u32 val; |
371 | 361 | ||
372 | mutex_lock(&fpc->lock); | 362 | mutex_lock(&fpc->lock); |
373 | val = readl(fpc->base + FTM_OUTMASK); | 363 | regmap_update_bits(fpc->regmap, FTM_OUTMASK, BIT(pwm->hwpwm), |
374 | val |= BIT(pwm->hwpwm); | 364 | BIT(pwm->hwpwm)); |
375 | writel(val, fpc->base + FTM_OUTMASK); | ||
376 | 365 | ||
377 | fsl_counter_clock_disable(fpc); | 366 | fsl_counter_clock_disable(fpc); |
378 | 367 | ||
379 | val = readl(fpc->base + FTM_OUTMASK); | 368 | regmap_read(fpc->regmap, FTM_OUTMASK, &val); |
380 | |||
381 | if ((val & 0xFF) == 0xFF) | 369 | if ((val & 0xFF) == 0xFF) |
382 | fpc->period_ns = 0; | 370 | fpc->period_ns = 0; |
383 | 371 | ||
@@ -402,19 +390,28 @@ static int fsl_pwm_init(struct fsl_pwm_chip *fpc) | |||
402 | if (ret) | 390 | if (ret) |
403 | return ret; | 391 | return ret; |
404 | 392 | ||
405 | writel(0x00, fpc->base + FTM_CNTIN); | 393 | regmap_write(fpc->regmap, FTM_CNTIN, 0x00); |
406 | writel(0x00, fpc->base + FTM_OUTINIT); | 394 | regmap_write(fpc->regmap, FTM_OUTINIT, 0x00); |
407 | writel(0xFF, fpc->base + FTM_OUTMASK); | 395 | regmap_write(fpc->regmap, FTM_OUTMASK, 0xFF); |
408 | 396 | ||
409 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); | 397 | clk_disable_unprepare(fpc->clk[FSL_PWM_CLK_SYS]); |
410 | 398 | ||
411 | return 0; | 399 | return 0; |
412 | } | 400 | } |
413 | 401 | ||
402 | static const struct regmap_config fsl_pwm_regmap_config = { | ||
403 | .reg_bits = 32, | ||
404 | .reg_stride = 4, | ||
405 | .val_bits = 32, | ||
406 | |||
407 | .max_register = FTM_PWMLOAD, | ||
408 | }; | ||
409 | |||
414 | static int fsl_pwm_probe(struct platform_device *pdev) | 410 | static int fsl_pwm_probe(struct platform_device *pdev) |
415 | { | 411 | { |
416 | struct fsl_pwm_chip *fpc; | 412 | struct fsl_pwm_chip *fpc; |
417 | struct resource *res; | 413 | struct resource *res; |
414 | void __iomem *base; | ||
418 | int ret; | 415 | int ret; |
419 | 416 | ||
420 | fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); | 417 | fpc = devm_kzalloc(&pdev->dev, sizeof(*fpc), GFP_KERNEL); |
@@ -426,9 +423,16 @@ static int fsl_pwm_probe(struct platform_device *pdev) | |||
426 | fpc->chip.dev = &pdev->dev; | 423 | fpc->chip.dev = &pdev->dev; |
427 | 424 | ||
428 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 425 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
429 | fpc->base = devm_ioremap_resource(&pdev->dev, res); | 426 | base = devm_ioremap_resource(&pdev->dev, res); |
430 | if (IS_ERR(fpc->base)) | 427 | if (IS_ERR(base)) |
431 | return PTR_ERR(fpc->base); | 428 | return PTR_ERR(base); |
429 | |||
430 | fpc->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, | ||
431 | &fsl_pwm_regmap_config); | ||
432 | if (IS_ERR(fpc->regmap)) { | ||
433 | dev_err(&pdev->dev, "regmap init failed\n"); | ||
434 | return PTR_ERR(fpc->regmap); | ||
435 | } | ||
432 | 436 | ||
433 | fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys"); | 437 | fpc->clk[FSL_PWM_CLK_SYS] = devm_clk_get(&pdev->dev, "ftm_sys"); |
434 | if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) { | 438 | if (IS_ERR(fpc->clk[FSL_PWM_CLK_SYS])) { |
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 5449d9150d40..f8b5f109c1ab 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/err.h> | 15 | #include <linux/err.h> |
16 | #include <linux/clk.h> | 16 | #include <linux/clk.h> |
17 | #include <linux/delay.h> | ||
17 | #include <linux/io.h> | 18 | #include <linux/io.h> |
18 | #include <linux/pwm.h> | 19 | #include <linux/pwm.h> |
19 | #include <linux/of.h> | 20 | #include <linux/of.h> |
@@ -21,24 +22,30 @@ | |||
21 | 22 | ||
22 | /* i.MX1 and i.MX21 share the same PWM function block: */ | 23 | /* i.MX1 and i.MX21 share the same PWM function block: */ |
23 | 24 | ||
24 | #define MX1_PWMC 0x00 /* PWM Control Register */ | 25 | #define MX1_PWMC 0x00 /* PWM Control Register */ |
25 | #define MX1_PWMS 0x04 /* PWM Sample Register */ | 26 | #define MX1_PWMS 0x04 /* PWM Sample Register */ |
26 | #define MX1_PWMP 0x08 /* PWM Period Register */ | 27 | #define MX1_PWMP 0x08 /* PWM Period Register */ |
27 | 28 | ||
28 | #define MX1_PWMC_EN (1 << 4) | 29 | #define MX1_PWMC_EN (1 << 4) |
29 | 30 | ||
30 | /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ | 31 | /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ |
31 | 32 | ||
32 | #define MX3_PWMCR 0x00 /* PWM Control Register */ | 33 | #define MX3_PWMCR 0x00 /* PWM Control Register */ |
33 | #define MX3_PWMSAR 0x0C /* PWM Sample Register */ | 34 | #define MX3_PWMSR 0x04 /* PWM Status Register */ |
34 | #define MX3_PWMPR 0x10 /* PWM Period Register */ | 35 | #define MX3_PWMSAR 0x0C /* PWM Sample Register */ |
35 | #define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4) | 36 | #define MX3_PWMPR 0x10 /* PWM Period Register */ |
36 | #define MX3_PWMCR_DOZEEN (1 << 24) | 37 | #define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4) |
37 | #define MX3_PWMCR_WAITEN (1 << 23) | 38 | #define MX3_PWMCR_DOZEEN (1 << 24) |
39 | #define MX3_PWMCR_WAITEN (1 << 23) | ||
38 | #define MX3_PWMCR_DBGEN (1 << 22) | 40 | #define MX3_PWMCR_DBGEN (1 << 22) |
39 | #define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) | 41 | #define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) |
40 | #define MX3_PWMCR_CLKSRC_IPG (1 << 16) | 42 | #define MX3_PWMCR_CLKSRC_IPG (1 << 16) |
41 | #define MX3_PWMCR_EN (1 << 0) | 43 | #define MX3_PWMCR_SWR (1 << 3) |
44 | #define MX3_PWMCR_EN (1 << 0) | ||
45 | #define MX3_PWMSR_FIFOAV_4WORDS 0x4 | ||
46 | #define MX3_PWMSR_FIFOAV_MASK 0x7 | ||
47 | |||
48 | #define MX3_PWM_SWR_LOOP 5 | ||
42 | 49 | ||
43 | struct imx_chip { | 50 | struct imx_chip { |
44 | struct clk *clk_per; | 51 | struct clk *clk_per; |
@@ -103,9 +110,43 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, | |||
103 | struct pwm_device *pwm, int duty_ns, int period_ns) | 110 | struct pwm_device *pwm, int duty_ns, int period_ns) |
104 | { | 111 | { |
105 | struct imx_chip *imx = to_imx_chip(chip); | 112 | struct imx_chip *imx = to_imx_chip(chip); |
113 | struct device *dev = chip->dev; | ||
106 | unsigned long long c; | 114 | unsigned long long c; |
107 | unsigned long period_cycles, duty_cycles, prescale; | 115 | unsigned long period_cycles, duty_cycles, prescale; |
108 | u32 cr; | 116 | unsigned int period_ms; |
117 | bool enable = test_bit(PWMF_ENABLED, &pwm->flags); | ||
118 | int wait_count = 0, fifoav; | ||
119 | u32 cr, sr; | ||
120 | |||
121 | /* | ||
122 | * i.MX PWMv2 has a 4-word sample FIFO. | ||
123 | * In order to avoid FIFO overflow issue, we do software reset | ||
124 | * to clear all sample FIFO if the controller is disabled or | ||
125 | * wait for a full PWM cycle to get a relinquished FIFO slot | ||
126 | * when the controller is enabled and the FIFO is fully loaded. | ||
127 | */ | ||
128 | if (enable) { | ||
129 | sr = readl(imx->mmio_base + MX3_PWMSR); | ||
130 | fifoav = sr & MX3_PWMSR_FIFOAV_MASK; | ||
131 | if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) { | ||
132 | period_ms = DIV_ROUND_UP(pwm->period, NSEC_PER_MSEC); | ||
133 | msleep(period_ms); | ||
134 | |||
135 | sr = readl(imx->mmio_base + MX3_PWMSR); | ||
136 | if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK)) | ||
137 | dev_warn(dev, "there is no free FIFO slot\n"); | ||
138 | } | ||
139 | } else { | ||
140 | writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR); | ||
141 | do { | ||
142 | usleep_range(200, 1000); | ||
143 | cr = readl(imx->mmio_base + MX3_PWMCR); | ||
144 | } while ((cr & MX3_PWMCR_SWR) && | ||
145 | (wait_count++ < MX3_PWM_SWR_LOOP)); | ||
146 | |||
147 | if (cr & MX3_PWMCR_SWR) | ||
148 | dev_warn(dev, "software reset timeout\n"); | ||
149 | } | ||
109 | 150 | ||
110 | c = clk_get_rate(imx->clk_per); | 151 | c = clk_get_rate(imx->clk_per); |
111 | c = c * period_ns; | 152 | c = c * period_ns; |
@@ -135,7 +176,7 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, | |||
135 | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | | 176 | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | |
136 | MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH; | 177 | MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH; |
137 | 178 | ||
138 | if (test_bit(PWMF_ENABLED, &pwm->flags)) | 179 | if (enable) |
139 | cr |= MX3_PWMCR_EN; | 180 | cr |= MX3_PWMCR_EN; |
140 | 181 | ||
141 | writel(cr, imx->mmio_base + MX3_PWMCR); | 182 | writel(cr, imx->mmio_base + MX3_PWMCR); |
diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c new file mode 100644 index 000000000000..cf20d2beacdd --- /dev/null +++ b/drivers/pwm/pwm-lpss-pci.c | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * Intel Low Power Subsystem PWM controller PCI driver | ||
3 | * | ||
4 | * Copyright (C) 2014, Intel Corporation | ||
5 | * | ||
6 | * Derived from the original pwm-lpss.c | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/pci.h> | ||
16 | |||
17 | #include "pwm-lpss.h" | ||
18 | |||
19 | static int pwm_lpss_probe_pci(struct pci_dev *pdev, | ||
20 | const struct pci_device_id *id) | ||
21 | { | ||
22 | const struct pwm_lpss_boardinfo *info; | ||
23 | struct pwm_lpss_chip *lpwm; | ||
24 | int err; | ||
25 | |||
26 | err = pcim_enable_device(pdev); | ||
27 | if (err < 0) | ||
28 | return err; | ||
29 | |||
30 | info = (struct pwm_lpss_boardinfo *)id->driver_data; | ||
31 | lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info); | ||
32 | if (IS_ERR(lpwm)) | ||
33 | return PTR_ERR(lpwm); | ||
34 | |||
35 | pci_set_drvdata(pdev, lpwm); | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static void pwm_lpss_remove_pci(struct pci_dev *pdev) | ||
40 | { | ||
41 | struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev); | ||
42 | |||
43 | pwm_lpss_remove(lpwm); | ||
44 | } | ||
45 | |||
46 | static const struct pci_device_id pwm_lpss_pci_ids[] = { | ||
47 | { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info}, | ||
48 | { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&pwm_lpss_byt_info}, | ||
49 | { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info}, | ||
50 | { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info}, | ||
51 | { }, | ||
52 | }; | ||
53 | MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids); | ||
54 | |||
55 | static struct pci_driver pwm_lpss_driver_pci = { | ||
56 | .name = "pwm-lpss", | ||
57 | .id_table = pwm_lpss_pci_ids, | ||
58 | .probe = pwm_lpss_probe_pci, | ||
59 | .remove = pwm_lpss_remove_pci, | ||
60 | }; | ||
61 | module_pci_driver(pwm_lpss_driver_pci); | ||
62 | |||
63 | MODULE_DESCRIPTION("PWM PCI driver for Intel LPSS"); | ||
64 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c new file mode 100644 index 000000000000..18a9c880a76d --- /dev/null +++ b/drivers/pwm/pwm-lpss-platform.c | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Intel Low Power Subsystem PWM controller driver | ||
3 | * | ||
4 | * Copyright (C) 2014, Intel Corporation | ||
5 | * | ||
6 | * Derived from the original pwm-lpss.c | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/acpi.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | |||
18 | #include "pwm-lpss.h" | ||
19 | |||
20 | static int pwm_lpss_probe_platform(struct platform_device *pdev) | ||
21 | { | ||
22 | const struct pwm_lpss_boardinfo *info; | ||
23 | const struct acpi_device_id *id; | ||
24 | struct pwm_lpss_chip *lpwm; | ||
25 | struct resource *r; | ||
26 | |||
27 | id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); | ||
28 | if (!id) | ||
29 | return -ENODEV; | ||
30 | |||
31 | info = (const struct pwm_lpss_boardinfo *)id->driver_data; | ||
32 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
33 | |||
34 | lpwm = pwm_lpss_probe(&pdev->dev, r, info); | ||
35 | if (IS_ERR(lpwm)) | ||
36 | return PTR_ERR(lpwm); | ||
37 | |||
38 | platform_set_drvdata(pdev, lpwm); | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | static int pwm_lpss_remove_platform(struct platform_device *pdev) | ||
43 | { | ||
44 | struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev); | ||
45 | |||
46 | return pwm_lpss_remove(lpwm); | ||
47 | } | ||
48 | |||
49 | static const struct acpi_device_id pwm_lpss_acpi_match[] = { | ||
50 | { "80860F09", (unsigned long)&pwm_lpss_byt_info }, | ||
51 | { "80862288", (unsigned long)&pwm_lpss_bsw_info }, | ||
52 | { }, | ||
53 | }; | ||
54 | MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); | ||
55 | |||
56 | static struct platform_driver pwm_lpss_driver_platform = { | ||
57 | .driver = { | ||
58 | .name = "pwm-lpss", | ||
59 | .acpi_match_table = pwm_lpss_acpi_match, | ||
60 | }, | ||
61 | .probe = pwm_lpss_probe_platform, | ||
62 | .remove = pwm_lpss_remove_platform, | ||
63 | }; | ||
64 | module_platform_driver(pwm_lpss_driver_platform); | ||
65 | |||
66 | MODULE_DESCRIPTION("PWM platform driver for Intel LPSS"); | ||
67 | MODULE_LICENSE("GPL v2"); | ||
68 | MODULE_ALIAS("platform:pwm-lpss"); | ||
diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 4df994f72d96..e9798253a16f 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c | |||
@@ -13,15 +13,11 @@ | |||
13 | * published by the Free Software Foundation. | 13 | * published by the Free Software Foundation. |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include <linux/acpi.h> | 16 | #include <linux/io.h> |
17 | #include <linux/device.h> | ||
18 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
19 | #include <linux/module.h> | 18 | #include <linux/module.h> |
20 | #include <linux/pwm.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/pci.h> | ||
23 | 19 | ||
24 | static int pci_drv, plat_drv; /* So we know which drivers registered */ | 20 | #include "pwm-lpss.h" |
25 | 21 | ||
26 | #define PWM 0x00000000 | 22 | #define PWM 0x00000000 |
27 | #define PWM_ENABLE BIT(31) | 23 | #define PWM_ENABLE BIT(31) |
@@ -39,14 +35,17 @@ struct pwm_lpss_chip { | |||
39 | unsigned long clk_rate; | 35 | unsigned long clk_rate; |
40 | }; | 36 | }; |
41 | 37 | ||
42 | struct pwm_lpss_boardinfo { | 38 | /* BayTrail */ |
43 | unsigned long clk_rate; | 39 | const struct pwm_lpss_boardinfo pwm_lpss_byt_info = { |
40 | .clk_rate = 25000000 | ||
44 | }; | 41 | }; |
42 | EXPORT_SYMBOL_GPL(pwm_lpss_byt_info); | ||
45 | 43 | ||
46 | /* BayTrail */ | 44 | /* Braswell */ |
47 | static const struct pwm_lpss_boardinfo byt_info = { | 45 | const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = { |
48 | 25000000 | 46 | .clk_rate = 19200000 |
49 | }; | 47 | }; |
48 | EXPORT_SYMBOL_GPL(pwm_lpss_bsw_info); | ||
50 | 49 | ||
51 | static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) | 50 | static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) |
52 | { | 51 | { |
@@ -118,9 +117,8 @@ static const struct pwm_ops pwm_lpss_ops = { | |||
118 | .owner = THIS_MODULE, | 117 | .owner = THIS_MODULE, |
119 | }; | 118 | }; |
120 | 119 | ||
121 | static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, | 120 | struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, |
122 | struct resource *r, | 121 | const struct pwm_lpss_boardinfo *info) |
123 | const struct pwm_lpss_boardinfo *info) | ||
124 | { | 122 | { |
125 | struct pwm_lpss_chip *lpwm; | 123 | struct pwm_lpss_chip *lpwm; |
126 | int ret; | 124 | int ret; |
@@ -147,8 +145,9 @@ static struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, | |||
147 | 145 | ||
148 | return lpwm; | 146 | return lpwm; |
149 | } | 147 | } |
148 | EXPORT_SYMBOL_GPL(pwm_lpss_probe); | ||
150 | 149 | ||
151 | static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) | 150 | int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) |
152 | { | 151 | { |
153 | u32 ctrl; | 152 | u32 ctrl; |
154 | 153 | ||
@@ -157,114 +156,8 @@ static int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) | |||
157 | 156 | ||
158 | return pwmchip_remove(&lpwm->chip); | 157 | return pwmchip_remove(&lpwm->chip); |
159 | } | 158 | } |
160 | 159 | EXPORT_SYMBOL_GPL(pwm_lpss_remove); | |
161 | static int pwm_lpss_probe_pci(struct pci_dev *pdev, | ||
162 | const struct pci_device_id *id) | ||
163 | { | ||
164 | const struct pwm_lpss_boardinfo *info; | ||
165 | struct pwm_lpss_chip *lpwm; | ||
166 | int err; | ||
167 | |||
168 | err = pci_enable_device(pdev); | ||
169 | if (err < 0) | ||
170 | return err; | ||
171 | |||
172 | info = (struct pwm_lpss_boardinfo *)id->driver_data; | ||
173 | lpwm = pwm_lpss_probe(&pdev->dev, &pdev->resource[0], info); | ||
174 | if (IS_ERR(lpwm)) | ||
175 | return PTR_ERR(lpwm); | ||
176 | |||
177 | pci_set_drvdata(pdev, lpwm); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static void pwm_lpss_remove_pci(struct pci_dev *pdev) | ||
182 | { | ||
183 | struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev); | ||
184 | |||
185 | pwm_lpss_remove(lpwm); | ||
186 | pci_disable_device(pdev); | ||
187 | } | ||
188 | |||
189 | static struct pci_device_id pwm_lpss_pci_ids[] = { | ||
190 | { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&byt_info}, | ||
191 | { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&byt_info}, | ||
192 | { }, | ||
193 | }; | ||
194 | MODULE_DEVICE_TABLE(pci, pwm_lpss_pci_ids); | ||
195 | |||
196 | static struct pci_driver pwm_lpss_driver_pci = { | ||
197 | .name = "pwm-lpss", | ||
198 | .id_table = pwm_lpss_pci_ids, | ||
199 | .probe = pwm_lpss_probe_pci, | ||
200 | .remove = pwm_lpss_remove_pci, | ||
201 | }; | ||
202 | |||
203 | static int pwm_lpss_probe_platform(struct platform_device *pdev) | ||
204 | { | ||
205 | const struct pwm_lpss_boardinfo *info; | ||
206 | const struct acpi_device_id *id; | ||
207 | struct pwm_lpss_chip *lpwm; | ||
208 | struct resource *r; | ||
209 | |||
210 | id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); | ||
211 | if (!id) | ||
212 | return -ENODEV; | ||
213 | |||
214 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
215 | |||
216 | info = (struct pwm_lpss_boardinfo *)id->driver_data; | ||
217 | lpwm = pwm_lpss_probe(&pdev->dev, r, info); | ||
218 | if (IS_ERR(lpwm)) | ||
219 | return PTR_ERR(lpwm); | ||
220 | |||
221 | platform_set_drvdata(pdev, lpwm); | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int pwm_lpss_remove_platform(struct platform_device *pdev) | ||
226 | { | ||
227 | struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev); | ||
228 | |||
229 | return pwm_lpss_remove(lpwm); | ||
230 | } | ||
231 | |||
232 | static const struct acpi_device_id pwm_lpss_acpi_match[] = { | ||
233 | { "80860F09", (unsigned long)&byt_info }, | ||
234 | { }, | ||
235 | }; | ||
236 | MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); | ||
237 | |||
238 | static struct platform_driver pwm_lpss_driver_platform = { | ||
239 | .driver = { | ||
240 | .name = "pwm-lpss", | ||
241 | .acpi_match_table = pwm_lpss_acpi_match, | ||
242 | }, | ||
243 | .probe = pwm_lpss_probe_platform, | ||
244 | .remove = pwm_lpss_remove_platform, | ||
245 | }; | ||
246 | |||
247 | static int __init pwm_init(void) | ||
248 | { | ||
249 | pci_drv = pci_register_driver(&pwm_lpss_driver_pci); | ||
250 | plat_drv = platform_driver_register(&pwm_lpss_driver_platform); | ||
251 | if (pci_drv && plat_drv) | ||
252 | return pci_drv; | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | module_init(pwm_init); | ||
257 | |||
258 | static void __exit pwm_exit(void) | ||
259 | { | ||
260 | if (!pci_drv) | ||
261 | pci_unregister_driver(&pwm_lpss_driver_pci); | ||
262 | if (!plat_drv) | ||
263 | platform_driver_unregister(&pwm_lpss_driver_platform); | ||
264 | } | ||
265 | module_exit(pwm_exit); | ||
266 | 160 | ||
267 | MODULE_DESCRIPTION("PWM driver for Intel LPSS"); | 161 | MODULE_DESCRIPTION("PWM driver for Intel LPSS"); |
268 | MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); | 162 | MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); |
269 | MODULE_LICENSE("GPL v2"); | 163 | MODULE_LICENSE("GPL v2"); |
270 | MODULE_ALIAS("platform:pwm-lpss"); | ||
diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h new file mode 100644 index 000000000000..aa041bb1b67d --- /dev/null +++ b/drivers/pwm/pwm-lpss.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * Intel Low Power Subsystem PWM controller driver | ||
3 | * | ||
4 | * Copyright (C) 2014, Intel Corporation | ||
5 | * | ||
6 | * Derived from the original pwm-lpss.c | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __PWM_LPSS_H | ||
14 | #define __PWM_LPSS_H | ||
15 | |||
16 | #include <linux/device.h> | ||
17 | #include <linux/pwm.h> | ||
18 | |||
19 | struct pwm_lpss_chip; | ||
20 | |||
21 | struct pwm_lpss_boardinfo { | ||
22 | unsigned long clk_rate; | ||
23 | }; | ||
24 | |||
25 | extern const struct pwm_lpss_boardinfo pwm_lpss_byt_info; | ||
26 | extern const struct pwm_lpss_boardinfo pwm_lpss_bsw_info; | ||
27 | |||
28 | struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, | ||
29 | const struct pwm_lpss_boardinfo *info); | ||
30 | int pwm_lpss_remove(struct pwm_lpss_chip *lpwm); | ||
31 | |||
32 | #endif /* __PWM_LPSS_H */ | ||
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index bdd8644c01cf..9442df244101 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c | |||
@@ -24,7 +24,9 @@ | |||
24 | #define PWM_ENABLE (1 << 0) | 24 | #define PWM_ENABLE (1 << 0) |
25 | #define PWM_CONTINUOUS (1 << 1) | 25 | #define PWM_CONTINUOUS (1 << 1) |
26 | #define PWM_DUTY_POSITIVE (1 << 3) | 26 | #define PWM_DUTY_POSITIVE (1 << 3) |
27 | #define PWM_DUTY_NEGATIVE (0 << 3) | ||
27 | #define PWM_INACTIVE_NEGATIVE (0 << 4) | 28 | #define PWM_INACTIVE_NEGATIVE (0 << 4) |
29 | #define PWM_INACTIVE_POSITIVE (1 << 4) | ||
28 | #define PWM_OUTPUT_LEFT (0 << 5) | 30 | #define PWM_OUTPUT_LEFT (0 << 5) |
29 | #define PWM_LP_DISABLE (0 << 8) | 31 | #define PWM_LP_DISABLE (0 << 8) |
30 | 32 | ||
@@ -45,8 +47,10 @@ struct rockchip_pwm_regs { | |||
45 | struct rockchip_pwm_data { | 47 | struct rockchip_pwm_data { |
46 | struct rockchip_pwm_regs regs; | 48 | struct rockchip_pwm_regs regs; |
47 | unsigned int prescaler; | 49 | unsigned int prescaler; |
50 | const struct pwm_ops *ops; | ||
48 | 51 | ||
49 | void (*set_enable)(struct pwm_chip *chip, bool enable); | 52 | void (*set_enable)(struct pwm_chip *chip, |
53 | struct pwm_device *pwm, bool enable); | ||
50 | }; | 54 | }; |
51 | 55 | ||
52 | static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) | 56 | static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) |
@@ -54,7 +58,8 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) | |||
54 | return container_of(c, struct rockchip_pwm_chip, chip); | 58 | return container_of(c, struct rockchip_pwm_chip, chip); |
55 | } | 59 | } |
56 | 60 | ||
57 | static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) | 61 | static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, |
62 | struct pwm_device *pwm, bool enable) | ||
58 | { | 63 | { |
59 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | 64 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); |
60 | u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; | 65 | u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; |
@@ -70,14 +75,19 @@ static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, bool enable) | |||
70 | writel_relaxed(val, pc->base + pc->data->regs.ctrl); | 75 | writel_relaxed(val, pc->base + pc->data->regs.ctrl); |
71 | } | 76 | } |
72 | 77 | ||
73 | static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) | 78 | static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, |
79 | struct pwm_device *pwm, bool enable) | ||
74 | { | 80 | { |
75 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | 81 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); |
76 | u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | | 82 | u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | |
77 | PWM_CONTINUOUS | PWM_DUTY_POSITIVE | | 83 | PWM_CONTINUOUS; |
78 | PWM_INACTIVE_NEGATIVE; | ||
79 | u32 val; | 84 | u32 val; |
80 | 85 | ||
86 | if (pwm->polarity == PWM_POLARITY_INVERSED) | ||
87 | enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; | ||
88 | else | ||
89 | enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; | ||
90 | |||
81 | val = readl_relaxed(pc->base + pc->data->regs.ctrl); | 91 | val = readl_relaxed(pc->base + pc->data->regs.ctrl); |
82 | 92 | ||
83 | if (enable) | 93 | if (enable) |
@@ -124,6 +134,19 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
124 | return 0; | 134 | return 0; |
125 | } | 135 | } |
126 | 136 | ||
137 | static int rockchip_pwm_set_polarity(struct pwm_chip *chip, | ||
138 | struct pwm_device *pwm, | ||
139 | enum pwm_polarity polarity) | ||
140 | { | ||
141 | /* | ||
142 | * No action needed here because pwm->polarity will be set by the core | ||
143 | * and the core will only change polarity when the PWM is not enabled. | ||
144 | * We'll handle things in set_enable(). | ||
145 | */ | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
127 | static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | 150 | static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
128 | { | 151 | { |
129 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | 152 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); |
@@ -133,7 +156,7 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
133 | if (ret) | 156 | if (ret) |
134 | return ret; | 157 | return ret; |
135 | 158 | ||
136 | pc->data->set_enable(chip, true); | 159 | pc->data->set_enable(chip, pwm, true); |
137 | 160 | ||
138 | return 0; | 161 | return 0; |
139 | } | 162 | } |
@@ -142,18 +165,26 @@ static void rockchip_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
142 | { | 165 | { |
143 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); | 166 | struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); |
144 | 167 | ||
145 | pc->data->set_enable(chip, false); | 168 | pc->data->set_enable(chip, pwm, false); |
146 | 169 | ||
147 | clk_disable(pc->clk); | 170 | clk_disable(pc->clk); |
148 | } | 171 | } |
149 | 172 | ||
150 | static const struct pwm_ops rockchip_pwm_ops = { | 173 | static const struct pwm_ops rockchip_pwm_ops_v1 = { |
151 | .config = rockchip_pwm_config, | 174 | .config = rockchip_pwm_config, |
152 | .enable = rockchip_pwm_enable, | 175 | .enable = rockchip_pwm_enable, |
153 | .disable = rockchip_pwm_disable, | 176 | .disable = rockchip_pwm_disable, |
154 | .owner = THIS_MODULE, | 177 | .owner = THIS_MODULE, |
155 | }; | 178 | }; |
156 | 179 | ||
180 | static const struct pwm_ops rockchip_pwm_ops_v2 = { | ||
181 | .config = rockchip_pwm_config, | ||
182 | .set_polarity = rockchip_pwm_set_polarity, | ||
183 | .enable = rockchip_pwm_enable, | ||
184 | .disable = rockchip_pwm_disable, | ||
185 | .owner = THIS_MODULE, | ||
186 | }; | ||
187 | |||
157 | static const struct rockchip_pwm_data pwm_data_v1 = { | 188 | static const struct rockchip_pwm_data pwm_data_v1 = { |
158 | .regs = { | 189 | .regs = { |
159 | .duty = 0x04, | 190 | .duty = 0x04, |
@@ -162,6 +193,7 @@ static const struct rockchip_pwm_data pwm_data_v1 = { | |||
162 | .ctrl = 0x0c, | 193 | .ctrl = 0x0c, |
163 | }, | 194 | }, |
164 | .prescaler = 2, | 195 | .prescaler = 2, |
196 | .ops = &rockchip_pwm_ops_v1, | ||
165 | .set_enable = rockchip_pwm_set_enable_v1, | 197 | .set_enable = rockchip_pwm_set_enable_v1, |
166 | }; | 198 | }; |
167 | 199 | ||
@@ -173,6 +205,7 @@ static const struct rockchip_pwm_data pwm_data_v2 = { | |||
173 | .ctrl = 0x0c, | 205 | .ctrl = 0x0c, |
174 | }, | 206 | }, |
175 | .prescaler = 1, | 207 | .prescaler = 1, |
208 | .ops = &rockchip_pwm_ops_v2, | ||
176 | .set_enable = rockchip_pwm_set_enable_v2, | 209 | .set_enable = rockchip_pwm_set_enable_v2, |
177 | }; | 210 | }; |
178 | 211 | ||
@@ -184,6 +217,7 @@ static const struct rockchip_pwm_data pwm_data_vop = { | |||
184 | .ctrl = 0x00, | 217 | .ctrl = 0x00, |
185 | }, | 218 | }, |
186 | .prescaler = 1, | 219 | .prescaler = 1, |
220 | .ops = &rockchip_pwm_ops_v2, | ||
187 | .set_enable = rockchip_pwm_set_enable_v2, | 221 | .set_enable = rockchip_pwm_set_enable_v2, |
188 | }; | 222 | }; |
189 | 223 | ||
@@ -227,10 +261,15 @@ static int rockchip_pwm_probe(struct platform_device *pdev) | |||
227 | 261 | ||
228 | pc->data = id->data; | 262 | pc->data = id->data; |
229 | pc->chip.dev = &pdev->dev; | 263 | pc->chip.dev = &pdev->dev; |
230 | pc->chip.ops = &rockchip_pwm_ops; | 264 | pc->chip.ops = pc->data->ops; |
231 | pc->chip.base = -1; | 265 | pc->chip.base = -1; |
232 | pc->chip.npwm = 1; | 266 | pc->chip.npwm = 1; |
233 | 267 | ||
268 | if (pc->data->ops->set_polarity) { | ||
269 | pc->chip.of_xlate = of_pwm_xlate_with_flags; | ||
270 | pc->chip.of_pwm_n_cells = 3; | ||
271 | } | ||
272 | |||
234 | ret = pwmchip_add(&pc->chip); | 273 | ret = pwmchip_add(&pc->chip); |
235 | if (ret < 0) { | 274 | if (ret < 0) { |
236 | clk_unprepare(pc->clk); | 275 | clk_unprepare(pc->clk); |
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8cd0beebdc3f..94ae1798d48a 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -830,7 +830,7 @@ config RTC_DRV_DA9063 | |||
830 | 830 | ||
831 | config RTC_DRV_EFI | 831 | config RTC_DRV_EFI |
832 | tristate "EFI RTC" | 832 | tristate "EFI RTC" |
833 | depends on EFI | 833 | depends on EFI && !X86 |
834 | help | 834 | help |
835 | If you say yes here you will get support for the EFI | 835 | If you say yes here you will get support for the EFI |
836 | Real Time Clock. | 836 | Real Time Clock. |
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index c384fec6d173..53b589dc34eb 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c | |||
@@ -236,3 +236,4 @@ MODULE_ALIAS("platform:rtc-efi"); | |||
236 | MODULE_AUTHOR("dann frazier <dannf@hp.com>"); | 236 | MODULE_AUTHOR("dann frazier <dannf@hp.com>"); |
237 | MODULE_LICENSE("GPL"); | 237 | MODULE_LICENSE("GPL"); |
238 | MODULE_DESCRIPTION("EFI RTC driver"); | 238 | MODULE_DESCRIPTION("EFI RTC driver"); |
239 | MODULE_ALIAS("platform:rtc-efi"); | ||
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index dc24ecfac2d1..db2cb1f8a1b5 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig | |||
@@ -105,7 +105,7 @@ config SCLP_ASYNC | |||
105 | config HMC_DRV | 105 | config HMC_DRV |
106 | def_tristate m | 106 | def_tristate m |
107 | prompt "Support for file transfers from HMC drive CD/DVD-ROM" | 107 | prompt "Support for file transfers from HMC drive CD/DVD-ROM" |
108 | depends on 64BIT | 108 | depends on S390 && 64BIT |
109 | select CRC16 | 109 | select CRC16 |
110 | help | 110 | help |
111 | This option enables support for file transfers from a Hardware | 111 | This option enables support for file transfers from a Hardware |
diff --git a/drivers/scsi/osd/Kbuild b/drivers/scsi/osd/Kbuild index 5fd73d77c3af..58cecd45b0f5 100644 --- a/drivers/scsi/osd/Kbuild +++ b/drivers/scsi/osd/Kbuild | |||
@@ -4,7 +4,7 @@ | |||
4 | # Copyright (C) 2008 Panasas Inc. All rights reserved. | 4 | # Copyright (C) 2008 Panasas Inc. All rights reserved. |
5 | # | 5 | # |
6 | # Authors: | 6 | # Authors: |
7 | # Boaz Harrosh <bharrosh@panasas.com> | 7 | # Boaz Harrosh <ooo@electrozaur.com> |
8 | # Benny Halevy <bhalevy@panasas.com> | 8 | # Benny Halevy <bhalevy@panasas.com> |
9 | # | 9 | # |
10 | # This program is free software; you can redistribute it and/or modify | 10 | # This program is free software; you can redistribute it and/or modify |
diff --git a/drivers/scsi/osd/Kconfig b/drivers/scsi/osd/Kconfig index a0703514eb0f..347cc5e33749 100644 --- a/drivers/scsi/osd/Kconfig +++ b/drivers/scsi/osd/Kconfig | |||
@@ -4,7 +4,7 @@ | |||
4 | # Copyright (C) 2008 Panasas Inc. All rights reserved. | 4 | # Copyright (C) 2008 Panasas Inc. All rights reserved. |
5 | # | 5 | # |
6 | # Authors: | 6 | # Authors: |
7 | # Boaz Harrosh <bharrosh@panasas.com> | 7 | # Boaz Harrosh <ooo@electrozaur.com> |
8 | # Benny Halevy <bhalevy@panasas.com> | 8 | # Benny Halevy <bhalevy@panasas.com> |
9 | # | 9 | # |
10 | # This program is free software; you can redistribute it and/or modify | 10 | # This program is free software; you can redistribute it and/or modify |
diff --git a/drivers/scsi/osd/osd_debug.h b/drivers/scsi/osd/osd_debug.h index 579e491f11df..26341261bb5c 100644 --- a/drivers/scsi/osd/osd_debug.h +++ b/drivers/scsi/osd/osd_debug.h | |||
@@ -4,7 +4,7 @@ | |||
4 | * Copyright (C) 2008 Panasas Inc. All rights reserved. | 4 | * Copyright (C) 2008 Panasas Inc. All rights reserved. |
5 | * | 5 | * |
6 | * Authors: | 6 | * Authors: |
7 | * Boaz Harrosh <bharrosh@panasas.com> | 7 | * Boaz Harrosh <ooo@electrozaur.com> |
8 | * Benny Halevy <bhalevy@panasas.com> | 8 | * Benny Halevy <bhalevy@panasas.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index fd19fd8468ac..488c3929f19a 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Copyright (C) 2008 Panasas Inc. All rights reserved. | 7 | * Copyright (C) 2008 Panasas Inc. All rights reserved. |
8 | * | 8 | * |
9 | * Authors: | 9 | * Authors: |
10 | * Boaz Harrosh <bharrosh@panasas.com> | 10 | * Boaz Harrosh <ooo@electrozaur.com> |
11 | * Benny Halevy <bhalevy@panasas.com> | 11 | * Benny Halevy <bhalevy@panasas.com> |
12 | * | 12 | * |
13 | * This program is free software; you can redistribute it and/or modify | 13 | * This program is free software; you can redistribute it and/or modify |
@@ -57,7 +57,7 @@ | |||
57 | 57 | ||
58 | enum { OSD_REQ_RETRIES = 1 }; | 58 | enum { OSD_REQ_RETRIES = 1 }; |
59 | 59 | ||
60 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); | 60 | MODULE_AUTHOR("Boaz Harrosh <ooo@electrozaur.com>"); |
61 | MODULE_DESCRIPTION("open-osd initiator library libosd.ko"); | 61 | MODULE_DESCRIPTION("open-osd initiator library libosd.ko"); |
62 | MODULE_LICENSE("GPL"); | 62 | MODULE_LICENSE("GPL"); |
63 | 63 | ||
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c index e1d9a4c4c4b3..92cdd4b06526 100644 --- a/drivers/scsi/osd/osd_uld.c +++ b/drivers/scsi/osd/osd_uld.c | |||
@@ -10,7 +10,7 @@ | |||
10 | * Copyright (C) 2008 Panasas Inc. All rights reserved. | 10 | * Copyright (C) 2008 Panasas Inc. All rights reserved. |
11 | * | 11 | * |
12 | * Authors: | 12 | * Authors: |
13 | * Boaz Harrosh <bharrosh@panasas.com> | 13 | * Boaz Harrosh <ooo@electrozaur.com> |
14 | * Benny Halevy <bhalevy@panasas.com> | 14 | * Benny Halevy <bhalevy@panasas.com> |
15 | * | 15 | * |
16 | * This program is free software; you can redistribute it and/or modify | 16 | * This program is free software; you can redistribute it and/or modify |
@@ -74,7 +74,7 @@ | |||
74 | static const char osd_name[] = "osd"; | 74 | static const char osd_name[] = "osd"; |
75 | static const char *osd_version_string = "open-osd 0.2.1"; | 75 | static const char *osd_version_string = "open-osd 0.2.1"; |
76 | 76 | ||
77 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); | 77 | MODULE_AUTHOR("Boaz Harrosh <ooo@electrozaur.com>"); |
78 | MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); | 78 | MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); |
79 | MODULE_LICENSE("GPL"); | 79 | MODULE_LICENSE("GPL"); |
80 | MODULE_ALIAS_CHARDEV_MAJOR(SCSI_OSD_MAJOR); | 80 | MODULE_ALIAS_CHARDEV_MAJOR(SCSI_OSD_MAJOR); |
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 829752cfd73f..a902fa1db7af 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c | |||
@@ -112,6 +112,7 @@ static void qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha, | |||
112 | struct qla_tgt_cmd *cmd); | 112 | struct qla_tgt_cmd *cmd); |
113 | static void qlt_alloc_qfull_cmd(struct scsi_qla_host *vha, | 113 | static void qlt_alloc_qfull_cmd(struct scsi_qla_host *vha, |
114 | struct atio_from_isp *atio, uint16_t status, int qfull); | 114 | struct atio_from_isp *atio, uint16_t status, int qfull); |
115 | static void qlt_disable_vha(struct scsi_qla_host *vha); | ||
115 | /* | 116 | /* |
116 | * Global Variables | 117 | * Global Variables |
117 | */ | 118 | */ |
@@ -210,7 +211,7 @@ static inline void qlt_decr_num_pend_cmds(struct scsi_qla_host *vha) | |||
210 | spin_unlock_irqrestore(&vha->hw->tgt.q_full_lock, flags); | 211 | spin_unlock_irqrestore(&vha->hw->tgt.q_full_lock, flags); |
211 | } | 212 | } |
212 | 213 | ||
213 | void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, | 214 | static void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, |
214 | struct atio_from_isp *atio) | 215 | struct atio_from_isp *atio) |
215 | { | 216 | { |
216 | ql_dbg(ql_dbg_tgt, vha, 0xe072, | 217 | ql_dbg(ql_dbg_tgt, vha, 0xe072, |
@@ -433,7 +434,7 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) | |||
433 | #if 0 /* FIXME: Re-enable Global event handling.. */ | 434 | #if 0 /* FIXME: Re-enable Global event handling.. */ |
434 | /* Global event */ | 435 | /* Global event */ |
435 | atomic_inc(&ha->tgt.qla_tgt->tgt_global_resets_count); | 436 | atomic_inc(&ha->tgt.qla_tgt->tgt_global_resets_count); |
436 | qlt_clear_tgt_db(ha->tgt.qla_tgt, 1); | 437 | qlt_clear_tgt_db(ha->tgt.qla_tgt); |
437 | if (!list_empty(&ha->tgt.qla_tgt->sess_list)) { | 438 | if (!list_empty(&ha->tgt.qla_tgt->sess_list)) { |
438 | sess = list_entry(ha->tgt.qla_tgt->sess_list.next, | 439 | sess = list_entry(ha->tgt.qla_tgt->sess_list.next, |
439 | typeof(*sess), sess_list_entry); | 440 | typeof(*sess), sess_list_entry); |
@@ -515,7 +516,7 @@ static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess, | |||
515 | } | 516 | } |
516 | 517 | ||
517 | /* ha->hardware_lock supposed to be held on entry */ | 518 | /* ha->hardware_lock supposed to be held on entry */ |
518 | static void qlt_clear_tgt_db(struct qla_tgt *tgt, bool local_only) | 519 | static void qlt_clear_tgt_db(struct qla_tgt *tgt) |
519 | { | 520 | { |
520 | struct qla_tgt_sess *sess; | 521 | struct qla_tgt_sess *sess; |
521 | 522 | ||
@@ -867,7 +868,7 @@ int qlt_stop_phase1(struct qla_tgt *tgt) | |||
867 | mutex_lock(&vha->vha_tgt.tgt_mutex); | 868 | mutex_lock(&vha->vha_tgt.tgt_mutex); |
868 | spin_lock_irqsave(&ha->hardware_lock, flags); | 869 | spin_lock_irqsave(&ha->hardware_lock, flags); |
869 | tgt->tgt_stop = 1; | 870 | tgt->tgt_stop = 1; |
870 | qlt_clear_tgt_db(tgt, true); | 871 | qlt_clear_tgt_db(tgt); |
871 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | 872 | spin_unlock_irqrestore(&ha->hardware_lock, flags); |
872 | mutex_unlock(&vha->vha_tgt.tgt_mutex); | 873 | mutex_unlock(&vha->vha_tgt.tgt_mutex); |
873 | mutex_unlock(&qla_tgt_mutex); | 874 | mutex_unlock(&qla_tgt_mutex); |
@@ -1462,12 +1463,13 @@ out_err: | |||
1462 | return -1; | 1463 | return -1; |
1463 | } | 1464 | } |
1464 | 1465 | ||
1465 | static inline void qlt_unmap_sg(struct scsi_qla_host *vha, | 1466 | static void qlt_unmap_sg(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd) |
1466 | struct qla_tgt_cmd *cmd) | ||
1467 | { | 1467 | { |
1468 | struct qla_hw_data *ha = vha->hw; | 1468 | struct qla_hw_data *ha = vha->hw; |
1469 | 1469 | ||
1470 | BUG_ON(!cmd->sg_mapped); | 1470 | if (!cmd->sg_mapped) |
1471 | return; | ||
1472 | |||
1471 | pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); | 1473 | pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); |
1472 | cmd->sg_mapped = 0; | 1474 | cmd->sg_mapped = 0; |
1473 | 1475 | ||
@@ -2428,8 +2430,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, | |||
2428 | return 0; | 2430 | return 0; |
2429 | 2431 | ||
2430 | out_unmap_unlock: | 2432 | out_unmap_unlock: |
2431 | if (cmd->sg_mapped) | 2433 | qlt_unmap_sg(vha, cmd); |
2432 | qlt_unmap_sg(vha, cmd); | ||
2433 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | 2434 | spin_unlock_irqrestore(&ha->hardware_lock, flags); |
2434 | 2435 | ||
2435 | return res; | 2436 | return res; |
@@ -2506,8 +2507,7 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) | |||
2506 | return res; | 2507 | return res; |
2507 | 2508 | ||
2508 | out_unlock_free_unmap: | 2509 | out_unlock_free_unmap: |
2509 | if (cmd->sg_mapped) | 2510 | qlt_unmap_sg(vha, cmd); |
2510 | qlt_unmap_sg(vha, cmd); | ||
2511 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | 2511 | spin_unlock_irqrestore(&ha->hardware_lock, flags); |
2512 | 2512 | ||
2513 | return res; | 2513 | return res; |
@@ -2741,8 +2741,7 @@ done: | |||
2741 | if (!ha_locked && !in_interrupt()) | 2741 | if (!ha_locked && !in_interrupt()) |
2742 | msleep(250); /* just in case */ | 2742 | msleep(250); /* just in case */ |
2743 | 2743 | ||
2744 | if (cmd->sg_mapped) | 2744 | qlt_unmap_sg(vha, cmd); |
2745 | qlt_unmap_sg(vha, cmd); | ||
2746 | vha->hw->tgt.tgt_ops->free_cmd(cmd); | 2745 | vha->hw->tgt.tgt_ops->free_cmd(cmd); |
2747 | } | 2746 | } |
2748 | return; | 2747 | return; |
@@ -3087,8 +3086,7 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, | |||
3087 | tfo = se_cmd->se_tfo; | 3086 | tfo = se_cmd->se_tfo; |
3088 | cmd->cmd_sent_to_fw = 0; | 3087 | cmd->cmd_sent_to_fw = 0; |
3089 | 3088 | ||
3090 | if (cmd->sg_mapped) | 3089 | qlt_unmap_sg(vha, cmd); |
3091 | qlt_unmap_sg(vha, cmd); | ||
3092 | 3090 | ||
3093 | if (unlikely(status != CTIO_SUCCESS)) { | 3091 | if (unlikely(status != CTIO_SUCCESS)) { |
3094 | switch (status & 0xFFFF) { | 3092 | switch (status & 0xFFFF) { |
@@ -5343,7 +5341,7 @@ void qlt_lport_deregister(struct scsi_qla_host *vha) | |||
5343 | EXPORT_SYMBOL(qlt_lport_deregister); | 5341 | EXPORT_SYMBOL(qlt_lport_deregister); |
5344 | 5342 | ||
5345 | /* Must be called under HW lock */ | 5343 | /* Must be called under HW lock */ |
5346 | void qlt_set_mode(struct scsi_qla_host *vha) | 5344 | static void qlt_set_mode(struct scsi_qla_host *vha) |
5347 | { | 5345 | { |
5348 | struct qla_hw_data *ha = vha->hw; | 5346 | struct qla_hw_data *ha = vha->hw; |
5349 | 5347 | ||
@@ -5364,7 +5362,7 @@ void qlt_set_mode(struct scsi_qla_host *vha) | |||
5364 | } | 5362 | } |
5365 | 5363 | ||
5366 | /* Must be called under HW lock */ | 5364 | /* Must be called under HW lock */ |
5367 | void qlt_clear_mode(struct scsi_qla_host *vha) | 5365 | static void qlt_clear_mode(struct scsi_qla_host *vha) |
5368 | { | 5366 | { |
5369 | struct qla_hw_data *ha = vha->hw; | 5367 | struct qla_hw_data *ha = vha->hw; |
5370 | 5368 | ||
@@ -5428,8 +5426,7 @@ EXPORT_SYMBOL(qlt_enable_vha); | |||
5428 | * | 5426 | * |
5429 | * Disable Target Mode and reset the adapter | 5427 | * Disable Target Mode and reset the adapter |
5430 | */ | 5428 | */ |
5431 | void | 5429 | static void qlt_disable_vha(struct scsi_qla_host *vha) |
5432 | qlt_disable_vha(struct scsi_qla_host *vha) | ||
5433 | { | 5430 | { |
5434 | struct qla_hw_data *ha = vha->hw; | 5431 | struct qla_hw_data *ha = vha->hw; |
5435 | struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; | 5432 | struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; |
diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h index 8ff330f7d6f5..332086776dfe 100644 --- a/drivers/scsi/qla2xxx/qla_target.h +++ b/drivers/scsi/qla2xxx/qla_target.h | |||
@@ -1001,11 +1001,11 @@ struct qla_tgt_prm { | |||
1001 | struct qla_tgt *tgt; | 1001 | struct qla_tgt *tgt; |
1002 | void *pkt; | 1002 | void *pkt; |
1003 | struct scatterlist *sg; /* cmd data buffer SG vector */ | 1003 | struct scatterlist *sg; /* cmd data buffer SG vector */ |
1004 | unsigned char *sense_buffer; | ||
1004 | int seg_cnt; | 1005 | int seg_cnt; |
1005 | int req_cnt; | 1006 | int req_cnt; |
1006 | uint16_t rq_result; | 1007 | uint16_t rq_result; |
1007 | uint16_t scsi_status; | 1008 | uint16_t scsi_status; |
1008 | unsigned char *sense_buffer; | ||
1009 | int sense_buffer_len; | 1009 | int sense_buffer_len; |
1010 | int residual; | 1010 | int residual; |
1011 | int add_status_pkt; | 1011 | int add_status_pkt; |
@@ -1033,10 +1033,6 @@ struct qla_tgt_srr_ctio { | |||
1033 | 1033 | ||
1034 | 1034 | ||
1035 | extern struct qla_tgt_data qla_target; | 1035 | extern struct qla_tgt_data qla_target; |
1036 | /* | ||
1037 | * Internal function prototypes | ||
1038 | */ | ||
1039 | void qlt_disable_vha(struct scsi_qla_host *); | ||
1040 | 1036 | ||
1041 | /* | 1037 | /* |
1042 | * Function prototypes for qla_target.c logic used by qla2xxx LLD code. | 1038 | * Function prototypes for qla_target.c logic used by qla2xxx LLD code. |
@@ -1049,8 +1045,6 @@ extern void qlt_lport_deregister(struct scsi_qla_host *); | |||
1049 | extern void qlt_unreg_sess(struct qla_tgt_sess *); | 1045 | extern void qlt_unreg_sess(struct qla_tgt_sess *); |
1050 | extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *); | 1046 | extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *); |
1051 | extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *); | 1047 | extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *); |
1052 | extern void qlt_set_mode(struct scsi_qla_host *ha); | ||
1053 | extern void qlt_clear_mode(struct scsi_qla_host *ha); | ||
1054 | extern int __init qlt_init(void); | 1048 | extern int __init qlt_init(void); |
1055 | extern void qlt_exit(void); | 1049 | extern void qlt_exit(void); |
1056 | extern void qlt_update_vp_map(struct scsi_qla_host *, int); | 1050 | extern void qlt_update_vp_map(struct scsi_qla_host *, int); |
@@ -1083,13 +1077,9 @@ static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha) | |||
1083 | /* | 1077 | /* |
1084 | * Exported symbols from qla_target.c LLD logic used by qla2xxx code.. | 1078 | * Exported symbols from qla_target.c LLD logic used by qla2xxx code.. |
1085 | */ | 1079 | */ |
1086 | extern void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *, | ||
1087 | struct atio_from_isp *); | ||
1088 | extern void qlt_response_pkt_all_vps(struct scsi_qla_host *, response_t *); | 1080 | extern void qlt_response_pkt_all_vps(struct scsi_qla_host *, response_t *); |
1089 | extern int qlt_rdy_to_xfer(struct qla_tgt_cmd *); | 1081 | extern int qlt_rdy_to_xfer(struct qla_tgt_cmd *); |
1090 | extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t); | 1082 | extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t); |
1091 | extern int qlt_rdy_to_xfer_dif(struct qla_tgt_cmd *); | ||
1092 | extern int qlt_xmit_response_dif(struct qla_tgt_cmd *, int, uint8_t); | ||
1093 | extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *); | 1083 | extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *); |
1094 | extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *); | 1084 | extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *); |
1095 | extern void qlt_free_cmd(struct qla_tgt_cmd *cmd); | 1085 | extern void qlt_free_cmd(struct qla_tgt_cmd *cmd); |
diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 031b2961c6b7..73f9feecda72 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c | |||
@@ -786,7 +786,16 @@ static void tcm_qla2xxx_clear_nacl_from_fcport_map(struct qla_tgt_sess *sess) | |||
786 | pr_debug("fc_rport domain: port_id 0x%06x\n", nacl->nport_id); | 786 | pr_debug("fc_rport domain: port_id 0x%06x\n", nacl->nport_id); |
787 | 787 | ||
788 | node = btree_remove32(&lport->lport_fcport_map, nacl->nport_id); | 788 | node = btree_remove32(&lport->lport_fcport_map, nacl->nport_id); |
789 | WARN_ON(node && (node != se_nacl)); | 789 | if (WARN_ON(node && (node != se_nacl))) { |
790 | /* | ||
791 | * The nacl no longer matches what we think it should be. | ||
792 | * Most likely a new dynamic acl has been added while | ||
793 | * someone dropped the hardware lock. It clearly is a | ||
794 | * bug elsewhere, but this bit can't make things worse. | ||
795 | */ | ||
796 | btree_insert32(&lport->lport_fcport_map, nacl->nport_id, | ||
797 | node, GFP_ATOMIC); | ||
798 | } | ||
790 | 799 | ||
791 | pr_debug("Removed from fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%06x\n", | 800 | pr_debug("Removed from fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%06x\n", |
792 | se_nacl, nacl->nport_wwnn, nacl->nport_id); | 801 | se_nacl, nacl->nport_wwnn, nacl->nport_id); |
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig index dc2d84ac5a0e..81d44c477a5b 100644 --- a/drivers/target/Kconfig +++ b/drivers/target/Kconfig | |||
@@ -31,6 +31,13 @@ config TCM_PSCSI | |||
31 | Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered | 31 | Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered |
32 | passthrough access to Linux/SCSI device | 32 | passthrough access to Linux/SCSI device |
33 | 33 | ||
34 | config TCM_USER | ||
35 | tristate "TCM/USER Subsystem Plugin for Linux" | ||
36 | depends on UIO && NET | ||
37 | help | ||
38 | Say Y here to enable the TCM/USER subsystem plugin for a userspace | ||
39 | process to handle requests | ||
40 | |||
34 | source "drivers/target/loopback/Kconfig" | 41 | source "drivers/target/loopback/Kconfig" |
35 | source "drivers/target/tcm_fc/Kconfig" | 42 | source "drivers/target/tcm_fc/Kconfig" |
36 | source "drivers/target/iscsi/Kconfig" | 43 | source "drivers/target/iscsi/Kconfig" |
diff --git a/drivers/target/Makefile b/drivers/target/Makefile index 85b012d2f89b..bbb4a7d638ef 100644 --- a/drivers/target/Makefile +++ b/drivers/target/Makefile | |||
@@ -22,6 +22,7 @@ obj-$(CONFIG_TARGET_CORE) += target_core_mod.o | |||
22 | obj-$(CONFIG_TCM_IBLOCK) += target_core_iblock.o | 22 | obj-$(CONFIG_TCM_IBLOCK) += target_core_iblock.o |
23 | obj-$(CONFIG_TCM_FILEIO) += target_core_file.o | 23 | obj-$(CONFIG_TCM_FILEIO) += target_core_file.o |
24 | obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o | 24 | obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o |
25 | obj-$(CONFIG_TCM_USER) += target_core_user.o | ||
25 | 26 | ||
26 | # Fabric modules | 27 | # Fabric modules |
27 | obj-$(CONFIG_LOOPBACK_TARGET) += loopback/ | 28 | obj-$(CONFIG_LOOPBACK_TARGET) += loopback/ |
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 260c3e1e312c..b19e4329ba00 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c | |||
@@ -3709,7 +3709,6 @@ static inline void iscsit_thread_check_cpumask( | |||
3709 | struct task_struct *p, | 3709 | struct task_struct *p, |
3710 | int mode) | 3710 | int mode) |
3711 | { | 3711 | { |
3712 | char buf[128]; | ||
3713 | /* | 3712 | /* |
3714 | * mode == 1 signals iscsi_target_tx_thread() usage. | 3713 | * mode == 1 signals iscsi_target_tx_thread() usage. |
3715 | * mode == 0 signals iscsi_target_rx_thread() usage. | 3714 | * mode == 0 signals iscsi_target_rx_thread() usage. |
@@ -3728,8 +3727,6 @@ static inline void iscsit_thread_check_cpumask( | |||
3728 | * both TX and RX kthreads are scheduled to run on the | 3727 | * both TX and RX kthreads are scheduled to run on the |
3729 | * same CPU. | 3728 | * same CPU. |
3730 | */ | 3729 | */ |
3731 | memset(buf, 0, 128); | ||
3732 | cpumask_scnprintf(buf, 128, conn->conn_cpumask); | ||
3733 | set_cpus_allowed_ptr(p, conn->conn_cpumask); | 3730 | set_cpus_allowed_ptr(p, conn->conn_cpumask); |
3734 | } | 3731 | } |
3735 | 3732 | ||
@@ -4326,8 +4323,7 @@ int iscsit_close_connection( | |||
4326 | if (conn->conn_tx_hash.tfm) | 4323 | if (conn->conn_tx_hash.tfm) |
4327 | crypto_free_hash(conn->conn_tx_hash.tfm); | 4324 | crypto_free_hash(conn->conn_tx_hash.tfm); |
4328 | 4325 | ||
4329 | if (conn->conn_cpumask) | 4326 | free_cpumask_var(conn->conn_cpumask); |
4330 | free_cpumask_var(conn->conn_cpumask); | ||
4331 | 4327 | ||
4332 | kfree(conn->conn_ops); | 4328 | kfree(conn->conn_ops); |
4333 | conn->conn_ops = NULL; | 4329 | conn->conn_ops = NULL; |
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index ae03f3e5de1e..9059c1e0b26e 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c | |||
@@ -669,12 +669,10 @@ static ssize_t lio_target_nacl_show_info( | |||
669 | } else { | 669 | } else { |
670 | sess = se_sess->fabric_sess_ptr; | 670 | sess = se_sess->fabric_sess_ptr; |
671 | 671 | ||
672 | if (sess->sess_ops->InitiatorName) | 672 | rb += sprintf(page+rb, "InitiatorName: %s\n", |
673 | rb += sprintf(page+rb, "InitiatorName: %s\n", | 673 | sess->sess_ops->InitiatorName); |
674 | sess->sess_ops->InitiatorName); | 674 | rb += sprintf(page+rb, "InitiatorAlias: %s\n", |
675 | if (sess->sess_ops->InitiatorAlias) | 675 | sess->sess_ops->InitiatorAlias); |
676 | rb += sprintf(page+rb, "InitiatorAlias: %s\n", | ||
677 | sess->sess_ops->InitiatorAlias); | ||
678 | 676 | ||
679 | rb += sprintf(page+rb, "LIO Session ID: %u " | 677 | rb += sprintf(page+rb, "LIO Session ID: %u " |
680 | "ISID: 0x%02x %02x %02x %02x %02x %02x " | 678 | "ISID: 0x%02x %02x %02x %02x %02x %02x " |
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index 0d1e6ee3e992..a0ae5fc0ad75 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c | |||
@@ -345,7 +345,6 @@ static int iscsit_dataout_check_datasn( | |||
345 | struct iscsi_cmd *cmd, | 345 | struct iscsi_cmd *cmd, |
346 | unsigned char *buf) | 346 | unsigned char *buf) |
347 | { | 347 | { |
348 | int dump = 0, recovery = 0; | ||
349 | u32 data_sn = 0; | 348 | u32 data_sn = 0; |
350 | struct iscsi_conn *conn = cmd->conn; | 349 | struct iscsi_conn *conn = cmd->conn; |
351 | struct iscsi_data *hdr = (struct iscsi_data *) buf; | 350 | struct iscsi_data *hdr = (struct iscsi_data *) buf; |
@@ -370,13 +369,11 @@ static int iscsit_dataout_check_datasn( | |||
370 | pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x" | 369 | pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x" |
371 | " higher than expected 0x%08x.\n", cmd->init_task_tag, | 370 | " higher than expected 0x%08x.\n", cmd->init_task_tag, |
372 | be32_to_cpu(hdr->datasn), data_sn); | 371 | be32_to_cpu(hdr->datasn), data_sn); |
373 | recovery = 1; | ||
374 | goto recover; | 372 | goto recover; |
375 | } else if (be32_to_cpu(hdr->datasn) < data_sn) { | 373 | } else if (be32_to_cpu(hdr->datasn) < data_sn) { |
376 | pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x" | 374 | pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x" |
377 | " lower than expected 0x%08x, discarding payload.\n", | 375 | " lower than expected 0x%08x, discarding payload.\n", |
378 | cmd->init_task_tag, be32_to_cpu(hdr->datasn), data_sn); | 376 | cmd->init_task_tag, be32_to_cpu(hdr->datasn), data_sn); |
379 | dump = 1; | ||
380 | goto dump; | 377 | goto dump; |
381 | } | 378 | } |
382 | 379 | ||
@@ -392,8 +389,7 @@ dump: | |||
392 | if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) | 389 | if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) |
393 | return DATAOUT_CANNOT_RECOVER; | 390 | return DATAOUT_CANNOT_RECOVER; |
394 | 391 | ||
395 | return (recovery || dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY : | 392 | return DATAOUT_WITHIN_COMMAND_RECOVERY; |
396 | DATAOUT_NORMAL; | ||
397 | } | 393 | } |
398 | 394 | ||
399 | static int iscsit_dataout_pre_datapduinorder_yes( | 395 | static int iscsit_dataout_pre_datapduinorder_yes( |
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 5e71ac609418..480f2e0ecc11 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c | |||
@@ -978,8 +978,7 @@ int iscsit_setup_np( | |||
978 | return 0; | 978 | return 0; |
979 | fail: | 979 | fail: |
980 | np->np_socket = NULL; | 980 | np->np_socket = NULL; |
981 | if (sock) | 981 | sock_release(sock); |
982 | sock_release(sock); | ||
983 | return ret; | 982 | return ret; |
984 | } | 983 | } |
985 | 984 | ||
@@ -1190,8 +1189,7 @@ old_sess_out: | |||
1190 | if (!IS_ERR(conn->conn_tx_hash.tfm)) | 1189 | if (!IS_ERR(conn->conn_tx_hash.tfm)) |
1191 | crypto_free_hash(conn->conn_tx_hash.tfm); | 1190 | crypto_free_hash(conn->conn_tx_hash.tfm); |
1192 | 1191 | ||
1193 | if (conn->conn_cpumask) | 1192 | free_cpumask_var(conn->conn_cpumask); |
1194 | free_cpumask_var(conn->conn_cpumask); | ||
1195 | 1193 | ||
1196 | kfree(conn->conn_ops); | 1194 | kfree(conn->conn_ops); |
1197 | 1195 | ||
@@ -1268,8 +1266,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) | |||
1268 | iscsit_put_transport(conn->conn_transport); | 1266 | iscsit_put_transport(conn->conn_transport); |
1269 | kfree(conn); | 1267 | kfree(conn); |
1270 | conn = NULL; | 1268 | conn = NULL; |
1271 | if (ret == -ENODEV) | ||
1272 | goto out; | ||
1273 | /* Get another socket */ | 1269 | /* Get another socket */ |
1274 | return 1; | 1270 | return 1; |
1275 | } | 1271 | } |
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 73355f4fca74..ce87ce9bdb9c 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c | |||
@@ -1481,8 +1481,9 @@ void iscsit_collect_login_stats( | |||
1481 | if (conn->param_list) | 1481 | if (conn->param_list) |
1482 | intrname = iscsi_find_param_from_key(INITIATORNAME, | 1482 | intrname = iscsi_find_param_from_key(INITIATORNAME, |
1483 | conn->param_list); | 1483 | conn->param_list); |
1484 | strcpy(ls->last_intr_fail_name, | 1484 | strlcpy(ls->last_intr_fail_name, |
1485 | (intrname ? intrname->value : "Unknown")); | 1485 | (intrname ? intrname->value : "Unknown"), |
1486 | sizeof(ls->last_intr_fail_name)); | ||
1486 | 1487 | ||
1487 | ls->last_intr_fail_ip_family = conn->login_family; | 1488 | ls->last_intr_fail_ip_family = conn->login_family; |
1488 | 1489 | ||
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 340de9d92b15..ab3ab27d49b7 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c | |||
@@ -153,18 +153,11 @@ static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag) | |||
153 | /* | 153 | /* |
154 | * Locate the SAM Task Attr from struct scsi_cmnd * | 154 | * Locate the SAM Task Attr from struct scsi_cmnd * |
155 | */ | 155 | */ |
156 | static int tcm_loop_sam_attr(struct scsi_cmnd *sc) | 156 | static int tcm_loop_sam_attr(struct scsi_cmnd *sc, int tag) |
157 | { | 157 | { |
158 | if (sc->device->tagged_supported) { | 158 | if (sc->device->tagged_supported && |
159 | switch (sc->tag) { | 159 | sc->device->ordered_tags && tag >= 0) |
160 | case HEAD_OF_QUEUE_TAG: | 160 | return MSG_ORDERED_TAG; |
161 | return MSG_HEAD_TAG; | ||
162 | case ORDERED_QUEUE_TAG: | ||
163 | return MSG_ORDERED_TAG; | ||
164 | default: | ||
165 | break; | ||
166 | } | ||
167 | } | ||
168 | 161 | ||
169 | return MSG_SIMPLE_TAG; | 162 | return MSG_SIMPLE_TAG; |
170 | } | 163 | } |
@@ -227,7 +220,7 @@ static void tcm_loop_submission_work(struct work_struct *work) | |||
227 | 220 | ||
228 | rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd, | 221 | rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd, |
229 | &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun, | 222 | &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun, |
230 | transfer_length, tcm_loop_sam_attr(sc), | 223 | transfer_length, tcm_loop_sam_attr(sc, tl_cmd->sc_cmd_tag), |
231 | sc->sc_data_direction, 0, | 224 | sc->sc_data_direction, 0, |
232 | scsi_sglist(sc), scsi_sg_count(sc), | 225 | scsi_sglist(sc), scsi_sg_count(sc), |
233 | sgl_bidi, sgl_bidi_count, | 226 | sgl_bidi, sgl_bidi_count, |
@@ -266,7 +259,7 @@ static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) | |||
266 | } | 259 | } |
267 | 260 | ||
268 | tl_cmd->sc = sc; | 261 | tl_cmd->sc = sc; |
269 | tl_cmd->sc_cmd_tag = sc->tag; | 262 | tl_cmd->sc_cmd_tag = sc->request->tag; |
270 | INIT_WORK(&tl_cmd->work, tcm_loop_submission_work); | 263 | INIT_WORK(&tl_cmd->work, tcm_loop_submission_work); |
271 | queue_work(tcm_loop_workqueue, &tl_cmd->work); | 264 | queue_work(tcm_loop_workqueue, &tl_cmd->work); |
272 | return 0; | 265 | return 0; |
@@ -370,7 +363,7 @@ static int tcm_loop_abort_task(struct scsi_cmnd *sc) | |||
370 | */ | 363 | */ |
371 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | 364 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; |
372 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, | 365 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, |
373 | sc->tag, TMR_ABORT_TASK); | 366 | sc->request->tag, TMR_ABORT_TASK); |
374 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; | 367 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; |
375 | } | 368 | } |
376 | 369 | ||
@@ -960,8 +953,7 @@ static int tcm_loop_port_link( | |||
960 | struct tcm_loop_tpg, tl_se_tpg); | 953 | struct tcm_loop_tpg, tl_se_tpg); |
961 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; | 954 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
962 | 955 | ||
963 | atomic_inc(&tl_tpg->tl_tpg_port_count); | 956 | atomic_inc_mb(&tl_tpg->tl_tpg_port_count); |
964 | smp_mb__after_atomic(); | ||
965 | /* | 957 | /* |
966 | * Add Linux/SCSI struct scsi_device by HCTL | 958 | * Add Linux/SCSI struct scsi_device by HCTL |
967 | */ | 959 | */ |
@@ -995,8 +987,7 @@ static void tcm_loop_port_unlink( | |||
995 | scsi_remove_device(sd); | 987 | scsi_remove_device(sd); |
996 | scsi_device_put(sd); | 988 | scsi_device_put(sd); |
997 | 989 | ||
998 | atomic_dec(&tl_tpg->tl_tpg_port_count); | 990 | atomic_dec_mb(&tl_tpg->tl_tpg_port_count); |
999 | smp_mb__after_atomic(); | ||
1000 | 991 | ||
1001 | pr_debug("TCM_Loop_ConfigFS: Port Unlink Successful\n"); | 992 | pr_debug("TCM_Loop_ConfigFS: Port Unlink Successful\n"); |
1002 | } | 993 | } |
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index fbc5ebb5f761..fb87780929d2 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c | |||
@@ -392,8 +392,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd) | |||
392 | if (tg_pt_id != tg_pt_gp->tg_pt_gp_id) | 392 | if (tg_pt_id != tg_pt_gp->tg_pt_gp_id) |
393 | continue; | 393 | continue; |
394 | 394 | ||
395 | atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); | 395 | atomic_inc_mb(&tg_pt_gp->tg_pt_gp_ref_cnt); |
396 | smp_mb__after_atomic(); | ||
397 | 396 | ||
398 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); | 397 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); |
399 | 398 | ||
@@ -403,8 +402,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd) | |||
403 | found = true; | 402 | found = true; |
404 | 403 | ||
405 | spin_lock(&dev->t10_alua.tg_pt_gps_lock); | 404 | spin_lock(&dev->t10_alua.tg_pt_gps_lock); |
406 | atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); | 405 | atomic_dec_mb(&tg_pt_gp->tg_pt_gp_ref_cnt); |
407 | smp_mb__after_atomic(); | ||
408 | break; | 406 | break; |
409 | } | 407 | } |
410 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); | 408 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); |
@@ -998,8 +996,7 @@ static void core_alua_do_transition_tg_pt_work(struct work_struct *work) | |||
998 | * every I_T nexus other than the I_T nexus on which the SET | 996 | * every I_T nexus other than the I_T nexus on which the SET |
999 | * TARGET PORT GROUPS command | 997 | * TARGET PORT GROUPS command |
1000 | */ | 998 | */ |
1001 | atomic_inc(&mem->tg_pt_gp_mem_ref_cnt); | 999 | atomic_inc_mb(&mem->tg_pt_gp_mem_ref_cnt); |
1002 | smp_mb__after_atomic(); | ||
1003 | spin_unlock(&tg_pt_gp->tg_pt_gp_lock); | 1000 | spin_unlock(&tg_pt_gp->tg_pt_gp_lock); |
1004 | 1001 | ||
1005 | spin_lock_bh(&port->sep_alua_lock); | 1002 | spin_lock_bh(&port->sep_alua_lock); |
@@ -1028,8 +1025,7 @@ static void core_alua_do_transition_tg_pt_work(struct work_struct *work) | |||
1028 | spin_unlock_bh(&port->sep_alua_lock); | 1025 | spin_unlock_bh(&port->sep_alua_lock); |
1029 | 1026 | ||
1030 | spin_lock(&tg_pt_gp->tg_pt_gp_lock); | 1027 | spin_lock(&tg_pt_gp->tg_pt_gp_lock); |
1031 | atomic_dec(&mem->tg_pt_gp_mem_ref_cnt); | 1028 | atomic_dec_mb(&mem->tg_pt_gp_mem_ref_cnt); |
1032 | smp_mb__after_atomic(); | ||
1033 | } | 1029 | } |
1034 | spin_unlock(&tg_pt_gp->tg_pt_gp_lock); | 1030 | spin_unlock(&tg_pt_gp->tg_pt_gp_lock); |
1035 | /* | 1031 | /* |
@@ -1063,7 +1059,6 @@ static void core_alua_do_transition_tg_pt_work(struct work_struct *work) | |||
1063 | core_alua_dump_state(tg_pt_gp->tg_pt_gp_alua_pending_state)); | 1059 | core_alua_dump_state(tg_pt_gp->tg_pt_gp_alua_pending_state)); |
1064 | spin_lock(&dev->t10_alua.tg_pt_gps_lock); | 1060 | spin_lock(&dev->t10_alua.tg_pt_gps_lock); |
1065 | atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); | 1061 | atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); |
1066 | smp_mb__after_atomic(); | ||
1067 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); | 1062 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); |
1068 | 1063 | ||
1069 | if (tg_pt_gp->tg_pt_gp_transition_complete) | 1064 | if (tg_pt_gp->tg_pt_gp_transition_complete) |
@@ -1125,7 +1120,6 @@ static int core_alua_do_transition_tg_pt( | |||
1125 | */ | 1120 | */ |
1126 | spin_lock(&dev->t10_alua.tg_pt_gps_lock); | 1121 | spin_lock(&dev->t10_alua.tg_pt_gps_lock); |
1127 | atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); | 1122 | atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); |
1128 | smp_mb__after_atomic(); | ||
1129 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); | 1123 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); |
1130 | 1124 | ||
1131 | if (!explicit && tg_pt_gp->tg_pt_gp_implicit_trans_secs) { | 1125 | if (!explicit && tg_pt_gp->tg_pt_gp_implicit_trans_secs) { |
@@ -1168,7 +1162,6 @@ int core_alua_do_port_transition( | |||
1168 | spin_lock(&local_lu_gp_mem->lu_gp_mem_lock); | 1162 | spin_lock(&local_lu_gp_mem->lu_gp_mem_lock); |
1169 | lu_gp = local_lu_gp_mem->lu_gp; | 1163 | lu_gp = local_lu_gp_mem->lu_gp; |
1170 | atomic_inc(&lu_gp->lu_gp_ref_cnt); | 1164 | atomic_inc(&lu_gp->lu_gp_ref_cnt); |
1171 | smp_mb__after_atomic(); | ||
1172 | spin_unlock(&local_lu_gp_mem->lu_gp_mem_lock); | 1165 | spin_unlock(&local_lu_gp_mem->lu_gp_mem_lock); |
1173 | /* | 1166 | /* |
1174 | * For storage objects that are members of the 'default_lu_gp', | 1167 | * For storage objects that are members of the 'default_lu_gp', |
@@ -1184,8 +1177,7 @@ int core_alua_do_port_transition( | |||
1184 | l_tg_pt_gp->tg_pt_gp_alua_nacl = l_nacl; | 1177 | l_tg_pt_gp->tg_pt_gp_alua_nacl = l_nacl; |
1185 | rc = core_alua_do_transition_tg_pt(l_tg_pt_gp, | 1178 | rc = core_alua_do_transition_tg_pt(l_tg_pt_gp, |
1186 | new_state, explicit); | 1179 | new_state, explicit); |
1187 | atomic_dec(&lu_gp->lu_gp_ref_cnt); | 1180 | atomic_dec_mb(&lu_gp->lu_gp_ref_cnt); |
1188 | smp_mb__after_atomic(); | ||
1189 | return rc; | 1181 | return rc; |
1190 | } | 1182 | } |
1191 | /* | 1183 | /* |
@@ -1198,8 +1190,7 @@ int core_alua_do_port_transition( | |||
1198 | lu_gp_mem_list) { | 1190 | lu_gp_mem_list) { |
1199 | 1191 | ||
1200 | dev = lu_gp_mem->lu_gp_mem_dev; | 1192 | dev = lu_gp_mem->lu_gp_mem_dev; |
1201 | atomic_inc(&lu_gp_mem->lu_gp_mem_ref_cnt); | 1193 | atomic_inc_mb(&lu_gp_mem->lu_gp_mem_ref_cnt); |
1202 | smp_mb__after_atomic(); | ||
1203 | spin_unlock(&lu_gp->lu_gp_lock); | 1194 | spin_unlock(&lu_gp->lu_gp_lock); |
1204 | 1195 | ||
1205 | spin_lock(&dev->t10_alua.tg_pt_gps_lock); | 1196 | spin_lock(&dev->t10_alua.tg_pt_gps_lock); |
@@ -1227,8 +1218,7 @@ int core_alua_do_port_transition( | |||
1227 | tg_pt_gp->tg_pt_gp_alua_port = NULL; | 1218 | tg_pt_gp->tg_pt_gp_alua_port = NULL; |
1228 | tg_pt_gp->tg_pt_gp_alua_nacl = NULL; | 1219 | tg_pt_gp->tg_pt_gp_alua_nacl = NULL; |
1229 | } | 1220 | } |
1230 | atomic_inc(&tg_pt_gp->tg_pt_gp_ref_cnt); | 1221 | atomic_inc_mb(&tg_pt_gp->tg_pt_gp_ref_cnt); |
1231 | smp_mb__after_atomic(); | ||
1232 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); | 1222 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); |
1233 | /* | 1223 | /* |
1234 | * core_alua_do_transition_tg_pt() will always return | 1224 | * core_alua_do_transition_tg_pt() will always return |
@@ -1238,16 +1228,14 @@ int core_alua_do_port_transition( | |||
1238 | new_state, explicit); | 1228 | new_state, explicit); |
1239 | 1229 | ||
1240 | spin_lock(&dev->t10_alua.tg_pt_gps_lock); | 1230 | spin_lock(&dev->t10_alua.tg_pt_gps_lock); |
1241 | atomic_dec(&tg_pt_gp->tg_pt_gp_ref_cnt); | 1231 | atomic_dec_mb(&tg_pt_gp->tg_pt_gp_ref_cnt); |
1242 | smp_mb__after_atomic(); | ||
1243 | if (rc) | 1232 | if (rc) |
1244 | break; | 1233 | break; |
1245 | } | 1234 | } |
1246 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); | 1235 | spin_unlock(&dev->t10_alua.tg_pt_gps_lock); |
1247 | 1236 | ||
1248 | spin_lock(&lu_gp->lu_gp_lock); | 1237 | spin_lock(&lu_gp->lu_gp_lock); |
1249 | atomic_dec(&lu_gp_mem->lu_gp_mem_ref_cnt); | 1238 | atomic_dec_mb(&lu_gp_mem->lu_gp_mem_ref_cnt); |
1250 | smp_mb__after_atomic(); | ||
1251 | } | 1239 | } |
1252 | spin_unlock(&lu_gp->lu_gp_lock); | 1240 | spin_unlock(&lu_gp->lu_gp_lock); |
1253 | 1241 | ||
@@ -1260,8 +1248,7 @@ int core_alua_do_port_transition( | |||
1260 | core_alua_dump_state(new_state)); | 1248 | core_alua_dump_state(new_state)); |
1261 | } | 1249 | } |
1262 | 1250 | ||
1263 | atomic_dec(&lu_gp->lu_gp_ref_cnt); | 1251 | atomic_dec_mb(&lu_gp->lu_gp_ref_cnt); |
1264 | smp_mb__after_atomic(); | ||
1265 | return rc; | 1252 | return rc; |
1266 | } | 1253 | } |
1267 | 1254 | ||
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 756def38c77a..79f9296a08ae 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c | |||
@@ -665,6 +665,9 @@ SE_DEV_ATTR(is_nonrot, S_IRUGO | S_IWUSR); | |||
665 | DEF_DEV_ATTRIB(emulate_rest_reord); | 665 | DEF_DEV_ATTRIB(emulate_rest_reord); |
666 | SE_DEV_ATTR(emulate_rest_reord, S_IRUGO | S_IWUSR); | 666 | SE_DEV_ATTR(emulate_rest_reord, S_IRUGO | S_IWUSR); |
667 | 667 | ||
668 | DEF_DEV_ATTRIB(force_pr_aptpl); | ||
669 | SE_DEV_ATTR(force_pr_aptpl, S_IRUGO | S_IWUSR); | ||
670 | |||
668 | DEF_DEV_ATTRIB_RO(hw_block_size); | 671 | DEF_DEV_ATTRIB_RO(hw_block_size); |
669 | SE_DEV_ATTR_RO(hw_block_size); | 672 | SE_DEV_ATTR_RO(hw_block_size); |
670 | 673 | ||
@@ -719,6 +722,7 @@ static struct configfs_attribute *target_core_dev_attrib_attrs[] = { | |||
719 | &target_core_dev_attrib_hw_pi_prot_type.attr, | 722 | &target_core_dev_attrib_hw_pi_prot_type.attr, |
720 | &target_core_dev_attrib_pi_prot_format.attr, | 723 | &target_core_dev_attrib_pi_prot_format.attr, |
721 | &target_core_dev_attrib_enforce_pr_isids.attr, | 724 | &target_core_dev_attrib_enforce_pr_isids.attr, |
725 | &target_core_dev_attrib_force_pr_aptpl.attr, | ||
722 | &target_core_dev_attrib_is_nonrot.attr, | 726 | &target_core_dev_attrib_is_nonrot.attr, |
723 | &target_core_dev_attrib_emulate_rest_reord.attr, | 727 | &target_core_dev_attrib_emulate_rest_reord.attr, |
724 | &target_core_dev_attrib_hw_block_size.attr, | 728 | &target_core_dev_attrib_hw_block_size.attr, |
@@ -1263,7 +1267,7 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( | |||
1263 | { | 1267 | { |
1264 | unsigned char *i_fabric = NULL, *i_port = NULL, *isid = NULL; | 1268 | unsigned char *i_fabric = NULL, *i_port = NULL, *isid = NULL; |
1265 | unsigned char *t_fabric = NULL, *t_port = NULL; | 1269 | unsigned char *t_fabric = NULL, *t_port = NULL; |
1266 | char *orig, *ptr, *arg_p, *opts; | 1270 | char *orig, *ptr, *opts; |
1267 | substring_t args[MAX_OPT_ARGS]; | 1271 | substring_t args[MAX_OPT_ARGS]; |
1268 | unsigned long long tmp_ll; | 1272 | unsigned long long tmp_ll; |
1269 | u64 sa_res_key = 0; | 1273 | u64 sa_res_key = 0; |
@@ -1295,14 +1299,14 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( | |||
1295 | token = match_token(ptr, tokens, args); | 1299 | token = match_token(ptr, tokens, args); |
1296 | switch (token) { | 1300 | switch (token) { |
1297 | case Opt_initiator_fabric: | 1301 | case Opt_initiator_fabric: |
1298 | i_fabric = match_strdup(&args[0]); | 1302 | i_fabric = match_strdup(args); |
1299 | if (!i_fabric) { | 1303 | if (!i_fabric) { |
1300 | ret = -ENOMEM; | 1304 | ret = -ENOMEM; |
1301 | goto out; | 1305 | goto out; |
1302 | } | 1306 | } |
1303 | break; | 1307 | break; |
1304 | case Opt_initiator_node: | 1308 | case Opt_initiator_node: |
1305 | i_port = match_strdup(&args[0]); | 1309 | i_port = match_strdup(args); |
1306 | if (!i_port) { | 1310 | if (!i_port) { |
1307 | ret = -ENOMEM; | 1311 | ret = -ENOMEM; |
1308 | goto out; | 1312 | goto out; |
@@ -1316,7 +1320,7 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( | |||
1316 | } | 1320 | } |
1317 | break; | 1321 | break; |
1318 | case Opt_initiator_sid: | 1322 | case Opt_initiator_sid: |
1319 | isid = match_strdup(&args[0]); | 1323 | isid = match_strdup(args); |
1320 | if (!isid) { | 1324 | if (!isid) { |
1321 | ret = -ENOMEM; | 1325 | ret = -ENOMEM; |
1322 | goto out; | 1326 | goto out; |
@@ -1330,15 +1334,9 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( | |||
1330 | } | 1334 | } |
1331 | break; | 1335 | break; |
1332 | case Opt_sa_res_key: | 1336 | case Opt_sa_res_key: |
1333 | arg_p = match_strdup(&args[0]); | 1337 | ret = kstrtoull(args->from, 0, &tmp_ll); |
1334 | if (!arg_p) { | ||
1335 | ret = -ENOMEM; | ||
1336 | goto out; | ||
1337 | } | ||
1338 | ret = kstrtoull(arg_p, 0, &tmp_ll); | ||
1339 | if (ret < 0) { | 1338 | if (ret < 0) { |
1340 | pr_err("kstrtoull() failed for" | 1339 | pr_err("kstrtoull() failed for sa_res_key=\n"); |
1341 | " sa_res_key=\n"); | ||
1342 | goto out; | 1340 | goto out; |
1343 | } | 1341 | } |
1344 | sa_res_key = (u64)tmp_ll; | 1342 | sa_res_key = (u64)tmp_ll; |
@@ -1370,14 +1368,14 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( | |||
1370 | * PR APTPL Metadata for Target Port | 1368 | * PR APTPL Metadata for Target Port |
1371 | */ | 1369 | */ |
1372 | case Opt_target_fabric: | 1370 | case Opt_target_fabric: |
1373 | t_fabric = match_strdup(&args[0]); | 1371 | t_fabric = match_strdup(args); |
1374 | if (!t_fabric) { | 1372 | if (!t_fabric) { |
1375 | ret = -ENOMEM; | 1373 | ret = -ENOMEM; |
1376 | goto out; | 1374 | goto out; |
1377 | } | 1375 | } |
1378 | break; | 1376 | break; |
1379 | case Opt_target_node: | 1377 | case Opt_target_node: |
1380 | t_port = match_strdup(&args[0]); | 1378 | t_port = match_strdup(args); |
1381 | if (!t_port) { | 1379 | if (!t_port) { |
1382 | ret = -ENOMEM; | 1380 | ret = -ENOMEM; |
1383 | goto out; | 1381 | goto out; |
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 98da90167159..c45f9e907e44 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c | |||
@@ -224,8 +224,7 @@ struct se_dev_entry *core_get_se_deve_from_rtpi( | |||
224 | if (port->sep_rtpi != rtpi) | 224 | if (port->sep_rtpi != rtpi) |
225 | continue; | 225 | continue; |
226 | 226 | ||
227 | atomic_inc(&deve->pr_ref_count); | 227 | atomic_inc_mb(&deve->pr_ref_count); |
228 | smp_mb__after_atomic(); | ||
229 | spin_unlock_irq(&nacl->device_list_lock); | 228 | spin_unlock_irq(&nacl->device_list_lock); |
230 | 229 | ||
231 | return deve; | 230 | return deve; |
@@ -1019,6 +1018,23 @@ int se_dev_set_enforce_pr_isids(struct se_device *dev, int flag) | |||
1019 | return 0; | 1018 | return 0; |
1020 | } | 1019 | } |
1021 | 1020 | ||
1021 | int se_dev_set_force_pr_aptpl(struct se_device *dev, int flag) | ||
1022 | { | ||
1023 | if ((flag != 0) && (flag != 1)) { | ||
1024 | printk(KERN_ERR "Illegal value %d\n", flag); | ||
1025 | return -EINVAL; | ||
1026 | } | ||
1027 | if (dev->export_count) { | ||
1028 | pr_err("dev[%p]: Unable to set force_pr_aptpl while" | ||
1029 | " export_count is %d\n", dev, dev->export_count); | ||
1030 | return -EINVAL; | ||
1031 | } | ||
1032 | |||
1033 | dev->dev_attrib.force_pr_aptpl = flag; | ||
1034 | pr_debug("dev[%p]: SE Device force_pr_aptpl: %d\n", dev, flag); | ||
1035 | return 0; | ||
1036 | } | ||
1037 | |||
1022 | int se_dev_set_is_nonrot(struct se_device *dev, int flag) | 1038 | int se_dev_set_is_nonrot(struct se_device *dev, int flag) |
1023 | { | 1039 | { |
1024 | if ((flag != 0) && (flag != 1)) { | 1040 | if ((flag != 0) && (flag != 1)) { |
@@ -1250,24 +1266,16 @@ struct se_lun *core_dev_add_lun( | |||
1250 | * | 1266 | * |
1251 | * | 1267 | * |
1252 | */ | 1268 | */ |
1253 | int core_dev_del_lun( | 1269 | void core_dev_del_lun( |
1254 | struct se_portal_group *tpg, | 1270 | struct se_portal_group *tpg, |
1255 | u32 unpacked_lun) | 1271 | struct se_lun *lun) |
1256 | { | 1272 | { |
1257 | struct se_lun *lun; | 1273 | pr_debug("%s_TPG[%u]_LUN[%u] - Deactivating %s Logical Unit from" |
1258 | |||
1259 | lun = core_tpg_pre_dellun(tpg, unpacked_lun); | ||
1260 | if (IS_ERR(lun)) | ||
1261 | return PTR_ERR(lun); | ||
1262 | |||
1263 | core_tpg_post_dellun(tpg, lun); | ||
1264 | |||
1265 | pr_debug("%s_TPG[%u]_LUN[%u] - Deactivated %s Logical Unit from" | ||
1266 | " device object\n", tpg->se_tpg_tfo->get_fabric_name(), | 1274 | " device object\n", tpg->se_tpg_tfo->get_fabric_name(), |
1267 | tpg->se_tpg_tfo->tpg_get_tag(tpg), unpacked_lun, | 1275 | tpg->se_tpg_tfo->tpg_get_tag(tpg), lun->unpacked_lun, |
1268 | tpg->se_tpg_tfo->get_fabric_name()); | 1276 | tpg->se_tpg_tfo->get_fabric_name()); |
1269 | 1277 | ||
1270 | return 0; | 1278 | core_tpg_remove_lun(tpg, lun); |
1271 | } | 1279 | } |
1272 | 1280 | ||
1273 | struct se_lun *core_get_lun_from_tpg(struct se_portal_group *tpg, u32 unpacked_lun) | 1281 | struct se_lun *core_get_lun_from_tpg(struct se_portal_group *tpg, u32 unpacked_lun) |
@@ -1396,8 +1404,7 @@ int core_dev_add_initiator_node_lun_acl( | |||
1396 | 1404 | ||
1397 | spin_lock(&lun->lun_acl_lock); | 1405 | spin_lock(&lun->lun_acl_lock); |
1398 | list_add_tail(&lacl->lacl_list, &lun->lun_acl_list); | 1406 | list_add_tail(&lacl->lacl_list, &lun->lun_acl_list); |
1399 | atomic_inc(&lun->lun_acl_count); | 1407 | atomic_inc_mb(&lun->lun_acl_count); |
1400 | smp_mb__after_atomic(); | ||
1401 | spin_unlock(&lun->lun_acl_lock); | 1408 | spin_unlock(&lun->lun_acl_lock); |
1402 | 1409 | ||
1403 | pr_debug("%s_TPG[%hu]_LUN[%u->%u] - Added %s ACL for " | 1410 | pr_debug("%s_TPG[%hu]_LUN[%u->%u] - Added %s ACL for " |
@@ -1409,7 +1416,8 @@ int core_dev_add_initiator_node_lun_acl( | |||
1409 | * Check to see if there are any existing persistent reservation APTPL | 1416 | * Check to see if there are any existing persistent reservation APTPL |
1410 | * pre-registrations that need to be enabled for this LUN ACL.. | 1417 | * pre-registrations that need to be enabled for this LUN ACL.. |
1411 | */ | 1418 | */ |
1412 | core_scsi3_check_aptpl_registration(lun->lun_se_dev, tpg, lun, lacl); | 1419 | core_scsi3_check_aptpl_registration(lun->lun_se_dev, tpg, lun, nacl, |
1420 | lacl->mapped_lun); | ||
1413 | return 0; | 1421 | return 0; |
1414 | } | 1422 | } |
1415 | 1423 | ||
@@ -1430,8 +1438,7 @@ int core_dev_del_initiator_node_lun_acl( | |||
1430 | 1438 | ||
1431 | spin_lock(&lun->lun_acl_lock); | 1439 | spin_lock(&lun->lun_acl_lock); |
1432 | list_del(&lacl->lacl_list); | 1440 | list_del(&lacl->lacl_list); |
1433 | atomic_dec(&lun->lun_acl_count); | 1441 | atomic_dec_mb(&lun->lun_acl_count); |
1434 | smp_mb__after_atomic(); | ||
1435 | spin_unlock(&lun->lun_acl_lock); | 1442 | spin_unlock(&lun->lun_acl_lock); |
1436 | 1443 | ||
1437 | core_disable_device_list_for_node(lun, NULL, lacl->mapped_lun, | 1444 | core_disable_device_list_for_node(lun, NULL, lacl->mapped_lun, |
@@ -1554,6 +1561,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) | |||
1554 | dev->dev_attrib.emulate_3pc = DA_EMULATE_3PC; | 1561 | dev->dev_attrib.emulate_3pc = DA_EMULATE_3PC; |
1555 | dev->dev_attrib.pi_prot_type = TARGET_DIF_TYPE0_PROT; | 1562 | dev->dev_attrib.pi_prot_type = TARGET_DIF_TYPE0_PROT; |
1556 | dev->dev_attrib.enforce_pr_isids = DA_ENFORCE_PR_ISIDS; | 1563 | dev->dev_attrib.enforce_pr_isids = DA_ENFORCE_PR_ISIDS; |
1564 | dev->dev_attrib.force_pr_aptpl = DA_FORCE_PR_APTPL; | ||
1557 | dev->dev_attrib.is_nonrot = DA_IS_NONROT; | 1565 | dev->dev_attrib.is_nonrot = DA_IS_NONROT; |
1558 | dev->dev_attrib.emulate_rest_reord = DA_EMULATE_REST_REORD; | 1566 | dev->dev_attrib.emulate_rest_reord = DA_EMULATE_REST_REORD; |
1559 | dev->dev_attrib.max_unmap_lba_count = DA_MAX_UNMAP_LBA_COUNT; | 1567 | dev->dev_attrib.max_unmap_lba_count = DA_MAX_UNMAP_LBA_COUNT; |
diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 7de9f0475d05..0c3f90130b7d 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c | |||
@@ -320,7 +320,7 @@ static struct config_group *target_fabric_make_mappedlun( | |||
320 | struct se_node_acl, acl_group); | 320 | struct se_node_acl, acl_group); |
321 | struct se_portal_group *se_tpg = se_nacl->se_tpg; | 321 | struct se_portal_group *se_tpg = se_nacl->se_tpg; |
322 | struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf; | 322 | struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf; |
323 | struct se_lun_acl *lacl; | 323 | struct se_lun_acl *lacl = NULL; |
324 | struct config_item *acl_ci; | 324 | struct config_item *acl_ci; |
325 | struct config_group *lacl_cg = NULL, *ml_stat_grp = NULL; | 325 | struct config_group *lacl_cg = NULL, *ml_stat_grp = NULL; |
326 | char *buf; | 326 | char *buf; |
@@ -406,6 +406,7 @@ static struct config_group *target_fabric_make_mappedlun( | |||
406 | out: | 406 | out: |
407 | if (lacl_cg) | 407 | if (lacl_cg) |
408 | kfree(lacl_cg->default_groups); | 408 | kfree(lacl_cg->default_groups); |
409 | kfree(lacl); | ||
409 | kfree(buf); | 410 | kfree(buf); |
410 | return ERR_PTR(ret); | 411 | return ERR_PTR(ret); |
411 | } | 412 | } |
@@ -821,7 +822,7 @@ static int target_fabric_port_unlink( | |||
821 | tf->tf_ops.fabric_pre_unlink(se_tpg, lun); | 822 | tf->tf_ops.fabric_pre_unlink(se_tpg, lun); |
822 | } | 823 | } |
823 | 824 | ||
824 | core_dev_del_lun(se_tpg, lun->unpacked_lun); | 825 | core_dev_del_lun(se_tpg, lun); |
825 | return 0; | 826 | return 0; |
826 | } | 827 | } |
827 | 828 | ||
@@ -910,16 +911,12 @@ static struct config_group *target_fabric_make_lun( | |||
910 | GFP_KERNEL); | 911 | GFP_KERNEL); |
911 | if (!port_stat_grp->default_groups) { | 912 | if (!port_stat_grp->default_groups) { |
912 | pr_err("Unable to allocate port_stat_grp->default_groups\n"); | 913 | pr_err("Unable to allocate port_stat_grp->default_groups\n"); |
913 | errno = -ENOMEM; | 914 | kfree(lun_cg->default_groups); |
914 | goto out; | 915 | return ERR_PTR(-ENOMEM); |
915 | } | 916 | } |
916 | target_stat_setup_port_default_groups(lun); | 917 | target_stat_setup_port_default_groups(lun); |
917 | 918 | ||
918 | return &lun->lun_group; | 919 | return &lun->lun_group; |
919 | out: | ||
920 | if (lun_cg) | ||
921 | kfree(lun_cg->default_groups); | ||
922 | return ERR_PTR(errno); | ||
923 | } | 920 | } |
924 | 921 | ||
925 | static void target_fabric_drop_lun( | 922 | static void target_fabric_drop_lun( |
diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index 0d1cf8b4f49f..35bfe77160d8 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c | |||
@@ -394,9 +394,9 @@ char *iscsi_parse_pr_out_transport_id( | |||
394 | * If the caller wants the TransportID Length, we set that value for the | 394 | * If the caller wants the TransportID Length, we set that value for the |
395 | * entire iSCSI Tarnsport ID now. | 395 | * entire iSCSI Tarnsport ID now. |
396 | */ | 396 | */ |
397 | if (out_tid_len != NULL) { | 397 | if (out_tid_len) { |
398 | add_len = ((buf[2] >> 8) & 0xff); | 398 | /* The shift works thanks to integer promotion rules */ |
399 | add_len |= (buf[3] & 0xff); | 399 | add_len = (buf[2] << 8) | buf[3]; |
400 | 400 | ||
401 | tid_len = strlen(&buf[4]); | 401 | tid_len = strlen(&buf[4]); |
402 | tid_len += 4; /* Add four bytes for iSCSI Transport ID header */ | 402 | tid_len += 4; /* Add four bytes for iSCSI Transport ID header */ |
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 7d6cddaec525..72c83d98662b 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c | |||
@@ -415,7 +415,7 @@ fd_execute_sync_cache(struct se_cmd *cmd) | |||
415 | } else { | 415 | } else { |
416 | start = cmd->t_task_lba * dev->dev_attrib.block_size; | 416 | start = cmd->t_task_lba * dev->dev_attrib.block_size; |
417 | if (cmd->data_length) | 417 | if (cmd->data_length) |
418 | end = start + cmd->data_length; | 418 | end = start + cmd->data_length - 1; |
419 | else | 419 | else |
420 | end = LLONG_MAX; | 420 | end = LLONG_MAX; |
421 | } | 421 | } |
@@ -680,7 +680,12 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, | |||
680 | struct fd_dev *fd_dev = FD_DEV(dev); | 680 | struct fd_dev *fd_dev = FD_DEV(dev); |
681 | loff_t start = cmd->t_task_lba * | 681 | loff_t start = cmd->t_task_lba * |
682 | dev->dev_attrib.block_size; | 682 | dev->dev_attrib.block_size; |
683 | loff_t end = start + cmd->data_length; | 683 | loff_t end; |
684 | |||
685 | if (cmd->data_length) | ||
686 | end = start + cmd->data_length - 1; | ||
687 | else | ||
688 | end = LLONG_MAX; | ||
684 | 689 | ||
685 | vfs_fsync_range(fd_dev->fd_file, start, end, 1); | 690 | vfs_fsync_range(fd_dev->fd_file, start, end, 1); |
686 | } | 691 | } |
@@ -762,7 +767,9 @@ static ssize_t fd_set_configfs_dev_params(struct se_device *dev, | |||
762 | fd_dev->fbd_flags |= FBDF_HAS_SIZE; | 767 | fd_dev->fbd_flags |= FBDF_HAS_SIZE; |
763 | break; | 768 | break; |
764 | case Opt_fd_buffered_io: | 769 | case Opt_fd_buffered_io: |
765 | match_int(args, &arg); | 770 | ret = match_int(args, &arg); |
771 | if (ret) | ||
772 | goto out; | ||
766 | if (arg != 1) { | 773 | if (arg != 1) { |
767 | pr_err("bogus fd_buffered_io=%d value\n", arg); | 774 | pr_err("bogus fd_buffered_io=%d value\n", arg); |
768 | ret = -EINVAL; | 775 | ret = -EINVAL; |
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index de9cab708f45..e31f42f369ff 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h | |||
@@ -38,6 +38,7 @@ int se_dev_set_emulate_3pc(struct se_device *, int); | |||
38 | int se_dev_set_pi_prot_type(struct se_device *, int); | 38 | int se_dev_set_pi_prot_type(struct se_device *, int); |
39 | int se_dev_set_pi_prot_format(struct se_device *, int); | 39 | int se_dev_set_pi_prot_format(struct se_device *, int); |
40 | int se_dev_set_enforce_pr_isids(struct se_device *, int); | 40 | int se_dev_set_enforce_pr_isids(struct se_device *, int); |
41 | int se_dev_set_force_pr_aptpl(struct se_device *, int); | ||
41 | int se_dev_set_is_nonrot(struct se_device *, int); | 42 | int se_dev_set_is_nonrot(struct se_device *, int); |
42 | int se_dev_set_emulate_rest_reord(struct se_device *dev, int); | 43 | int se_dev_set_emulate_rest_reord(struct se_device *dev, int); |
43 | int se_dev_set_queue_depth(struct se_device *, u32); | 44 | int se_dev_set_queue_depth(struct se_device *, u32); |
@@ -46,7 +47,7 @@ int se_dev_set_fabric_max_sectors(struct se_device *, u32); | |||
46 | int se_dev_set_optimal_sectors(struct se_device *, u32); | 47 | int se_dev_set_optimal_sectors(struct se_device *, u32); |
47 | int se_dev_set_block_size(struct se_device *, u32); | 48 | int se_dev_set_block_size(struct se_device *, u32); |
48 | struct se_lun *core_dev_add_lun(struct se_portal_group *, struct se_device *, u32); | 49 | struct se_lun *core_dev_add_lun(struct se_portal_group *, struct se_device *, u32); |
49 | int core_dev_del_lun(struct se_portal_group *, u32); | 50 | void core_dev_del_lun(struct se_portal_group *, struct se_lun *); |
50 | struct se_lun *core_get_lun_from_tpg(struct se_portal_group *, u32); | 51 | struct se_lun *core_get_lun_from_tpg(struct se_portal_group *, u32); |
51 | struct se_lun_acl *core_dev_init_initiator_node_lun_acl(struct se_portal_group *, | 52 | struct se_lun_acl *core_dev_init_initiator_node_lun_acl(struct se_portal_group *, |
52 | struct se_node_acl *, u32, int *); | 53 | struct se_node_acl *, u32, int *); |
@@ -82,8 +83,7 @@ void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *); | |||
82 | struct se_lun *core_tpg_alloc_lun(struct se_portal_group *, u32); | 83 | struct se_lun *core_tpg_alloc_lun(struct se_portal_group *, u32); |
83 | int core_tpg_add_lun(struct se_portal_group *, struct se_lun *, | 84 | int core_tpg_add_lun(struct se_portal_group *, struct se_lun *, |
84 | u32, struct se_device *); | 85 | u32, struct se_device *); |
85 | struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32 unpacked_lun); | 86 | void core_tpg_remove_lun(struct se_portal_group *, struct se_lun *); |
86 | int core_tpg_post_dellun(struct se_portal_group *, struct se_lun *); | ||
87 | 87 | ||
88 | /* target_core_transport.c */ | 88 | /* target_core_transport.c */ |
89 | extern struct kmem_cache *se_tmr_req_cache; | 89 | extern struct kmem_cache *se_tmr_req_cache; |
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index df357862286e..8c60a1a1ae8d 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c | |||
@@ -674,8 +674,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( | |||
674 | */ | 674 | */ |
675 | spin_lock(&dev->se_port_lock); | 675 | spin_lock(&dev->se_port_lock); |
676 | list_for_each_entry_safe(port, port_tmp, &dev->dev_sep_list, sep_list) { | 676 | list_for_each_entry_safe(port, port_tmp, &dev->dev_sep_list, sep_list) { |
677 | atomic_inc(&port->sep_tg_pt_ref_cnt); | 677 | atomic_inc_mb(&port->sep_tg_pt_ref_cnt); |
678 | smp_mb__after_atomic(); | ||
679 | spin_unlock(&dev->se_port_lock); | 678 | spin_unlock(&dev->se_port_lock); |
680 | 679 | ||
681 | spin_lock_bh(&port->sep_alua_lock); | 680 | spin_lock_bh(&port->sep_alua_lock); |
@@ -709,8 +708,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( | |||
709 | if (strcmp(nacl->initiatorname, nacl_tmp->initiatorname)) | 708 | if (strcmp(nacl->initiatorname, nacl_tmp->initiatorname)) |
710 | continue; | 709 | continue; |
711 | 710 | ||
712 | atomic_inc(&deve_tmp->pr_ref_count); | 711 | atomic_inc_mb(&deve_tmp->pr_ref_count); |
713 | smp_mb__after_atomic(); | ||
714 | spin_unlock_bh(&port->sep_alua_lock); | 712 | spin_unlock_bh(&port->sep_alua_lock); |
715 | /* | 713 | /* |
716 | * Grab a configfs group dependency that is released | 714 | * Grab a configfs group dependency that is released |
@@ -722,10 +720,8 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( | |||
722 | if (ret < 0) { | 720 | if (ret < 0) { |
723 | pr_err("core_scsi3_lunacl_depend" | 721 | pr_err("core_scsi3_lunacl_depend" |
724 | "_item() failed\n"); | 722 | "_item() failed\n"); |
725 | atomic_dec(&port->sep_tg_pt_ref_cnt); | 723 | atomic_dec_mb(&port->sep_tg_pt_ref_cnt); |
726 | smp_mb__after_atomic(); | 724 | atomic_dec_mb(&deve_tmp->pr_ref_count); |
727 | atomic_dec(&deve_tmp->pr_ref_count); | ||
728 | smp_mb__after_atomic(); | ||
729 | goto out; | 725 | goto out; |
730 | } | 726 | } |
731 | /* | 727 | /* |
@@ -739,10 +735,8 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( | |||
739 | nacl_tmp, deve_tmp, NULL, | 735 | nacl_tmp, deve_tmp, NULL, |
740 | sa_res_key, all_tg_pt, aptpl); | 736 | sa_res_key, all_tg_pt, aptpl); |
741 | if (!pr_reg_atp) { | 737 | if (!pr_reg_atp) { |
742 | atomic_dec(&port->sep_tg_pt_ref_cnt); | 738 | atomic_dec_mb(&port->sep_tg_pt_ref_cnt); |
743 | smp_mb__after_atomic(); | 739 | atomic_dec_mb(&deve_tmp->pr_ref_count); |
744 | atomic_dec(&deve_tmp->pr_ref_count); | ||
745 | smp_mb__after_atomic(); | ||
746 | core_scsi3_lunacl_undepend_item(deve_tmp); | 740 | core_scsi3_lunacl_undepend_item(deve_tmp); |
747 | goto out; | 741 | goto out; |
748 | } | 742 | } |
@@ -754,8 +748,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( | |||
754 | spin_unlock_bh(&port->sep_alua_lock); | 748 | spin_unlock_bh(&port->sep_alua_lock); |
755 | 749 | ||
756 | spin_lock(&dev->se_port_lock); | 750 | spin_lock(&dev->se_port_lock); |
757 | atomic_dec(&port->sep_tg_pt_ref_cnt); | 751 | atomic_dec_mb(&port->sep_tg_pt_ref_cnt); |
758 | smp_mb__after_atomic(); | ||
759 | } | 752 | } |
760 | spin_unlock(&dev->se_port_lock); | 753 | spin_unlock(&dev->se_port_lock); |
761 | 754 | ||
@@ -902,6 +895,7 @@ static int __core_scsi3_check_aptpl_registration( | |||
902 | spin_lock(&pr_tmpl->aptpl_reg_lock); | 895 | spin_lock(&pr_tmpl->aptpl_reg_lock); |
903 | list_for_each_entry_safe(pr_reg, pr_reg_tmp, &pr_tmpl->aptpl_reg_list, | 896 | list_for_each_entry_safe(pr_reg, pr_reg_tmp, &pr_tmpl->aptpl_reg_list, |
904 | pr_reg_aptpl_list) { | 897 | pr_reg_aptpl_list) { |
898 | |||
905 | if (!strcmp(pr_reg->pr_iport, i_port) && | 899 | if (!strcmp(pr_reg->pr_iport, i_port) && |
906 | (pr_reg->pr_res_mapped_lun == deve->mapped_lun) && | 900 | (pr_reg->pr_res_mapped_lun == deve->mapped_lun) && |
907 | !(strcmp(pr_reg->pr_tport, t_port)) && | 901 | !(strcmp(pr_reg->pr_tport, t_port)) && |
@@ -944,10 +938,10 @@ int core_scsi3_check_aptpl_registration( | |||
944 | struct se_device *dev, | 938 | struct se_device *dev, |
945 | struct se_portal_group *tpg, | 939 | struct se_portal_group *tpg, |
946 | struct se_lun *lun, | 940 | struct se_lun *lun, |
947 | struct se_lun_acl *lun_acl) | 941 | struct se_node_acl *nacl, |
942 | u32 mapped_lun) | ||
948 | { | 943 | { |
949 | struct se_node_acl *nacl = lun_acl->se_lun_nacl; | 944 | struct se_dev_entry *deve = nacl->device_list[mapped_lun]; |
950 | struct se_dev_entry *deve = nacl->device_list[lun_acl->mapped_lun]; | ||
951 | 945 | ||
952 | if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) | 946 | if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS) |
953 | return 0; | 947 | return 0; |
@@ -1109,8 +1103,7 @@ static struct t10_pr_registration *__core_scsi3_locate_pr_reg( | |||
1109 | if (dev->dev_attrib.enforce_pr_isids) | 1103 | if (dev->dev_attrib.enforce_pr_isids) |
1110 | continue; | 1104 | continue; |
1111 | } | 1105 | } |
1112 | atomic_inc(&pr_reg->pr_res_holders); | 1106 | atomic_inc_mb(&pr_reg->pr_res_holders); |
1113 | smp_mb__after_atomic(); | ||
1114 | spin_unlock(&pr_tmpl->registration_lock); | 1107 | spin_unlock(&pr_tmpl->registration_lock); |
1115 | return pr_reg; | 1108 | return pr_reg; |
1116 | } | 1109 | } |
@@ -1124,8 +1117,7 @@ static struct t10_pr_registration *__core_scsi3_locate_pr_reg( | |||
1124 | if (strcmp(isid, pr_reg->pr_reg_isid)) | 1117 | if (strcmp(isid, pr_reg->pr_reg_isid)) |
1125 | continue; | 1118 | continue; |
1126 | 1119 | ||
1127 | atomic_inc(&pr_reg->pr_res_holders); | 1120 | atomic_inc_mb(&pr_reg->pr_res_holders); |
1128 | smp_mb__after_atomic(); | ||
1129 | spin_unlock(&pr_tmpl->registration_lock); | 1121 | spin_unlock(&pr_tmpl->registration_lock); |
1130 | return pr_reg; | 1122 | return pr_reg; |
1131 | } | 1123 | } |
@@ -1154,8 +1146,7 @@ static struct t10_pr_registration *core_scsi3_locate_pr_reg( | |||
1154 | 1146 | ||
1155 | static void core_scsi3_put_pr_reg(struct t10_pr_registration *pr_reg) | 1147 | static void core_scsi3_put_pr_reg(struct t10_pr_registration *pr_reg) |
1156 | { | 1148 | { |
1157 | atomic_dec(&pr_reg->pr_res_holders); | 1149 | atomic_dec_mb(&pr_reg->pr_res_holders); |
1158 | smp_mb__after_atomic(); | ||
1159 | } | 1150 | } |
1160 | 1151 | ||
1161 | static int core_scsi3_check_implicit_release( | 1152 | static int core_scsi3_check_implicit_release( |
@@ -1348,8 +1339,7 @@ static void core_scsi3_tpg_undepend_item(struct se_portal_group *tpg) | |||
1348 | configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys, | 1339 | configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys, |
1349 | &tpg->tpg_group.cg_item); | 1340 | &tpg->tpg_group.cg_item); |
1350 | 1341 | ||
1351 | atomic_dec(&tpg->tpg_pr_ref_count); | 1342 | atomic_dec_mb(&tpg->tpg_pr_ref_count); |
1352 | smp_mb__after_atomic(); | ||
1353 | } | 1343 | } |
1354 | 1344 | ||
1355 | static int core_scsi3_nodeacl_depend_item(struct se_node_acl *nacl) | 1345 | static int core_scsi3_nodeacl_depend_item(struct se_node_acl *nacl) |
@@ -1368,16 +1358,14 @@ static void core_scsi3_nodeacl_undepend_item(struct se_node_acl *nacl) | |||
1368 | struct se_portal_group *tpg = nacl->se_tpg; | 1358 | struct se_portal_group *tpg = nacl->se_tpg; |
1369 | 1359 | ||
1370 | if (nacl->dynamic_node_acl) { | 1360 | if (nacl->dynamic_node_acl) { |
1371 | atomic_dec(&nacl->acl_pr_ref_count); | 1361 | atomic_dec_mb(&nacl->acl_pr_ref_count); |
1372 | smp_mb__after_atomic(); | ||
1373 | return; | 1362 | return; |
1374 | } | 1363 | } |
1375 | 1364 | ||
1376 | configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys, | 1365 | configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys, |
1377 | &nacl->acl_group.cg_item); | 1366 | &nacl->acl_group.cg_item); |
1378 | 1367 | ||
1379 | atomic_dec(&nacl->acl_pr_ref_count); | 1368 | atomic_dec_mb(&nacl->acl_pr_ref_count); |
1380 | smp_mb__after_atomic(); | ||
1381 | } | 1369 | } |
1382 | 1370 | ||
1383 | static int core_scsi3_lunacl_depend_item(struct se_dev_entry *se_deve) | 1371 | static int core_scsi3_lunacl_depend_item(struct se_dev_entry *se_deve) |
@@ -1407,8 +1395,7 @@ static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve) | |||
1407 | * For nacl->dynamic_node_acl=1 | 1395 | * For nacl->dynamic_node_acl=1 |
1408 | */ | 1396 | */ |
1409 | if (!lun_acl) { | 1397 | if (!lun_acl) { |
1410 | atomic_dec(&se_deve->pr_ref_count); | 1398 | atomic_dec_mb(&se_deve->pr_ref_count); |
1411 | smp_mb__after_atomic(); | ||
1412 | return; | 1399 | return; |
1413 | } | 1400 | } |
1414 | nacl = lun_acl->se_lun_nacl; | 1401 | nacl = lun_acl->se_lun_nacl; |
@@ -1417,8 +1404,7 @@ static void core_scsi3_lunacl_undepend_item(struct se_dev_entry *se_deve) | |||
1417 | configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys, | 1404 | configfs_undepend_item(tpg->se_tpg_tfo->tf_subsys, |
1418 | &lun_acl->se_lun_group.cg_item); | 1405 | &lun_acl->se_lun_group.cg_item); |
1419 | 1406 | ||
1420 | atomic_dec(&se_deve->pr_ref_count); | 1407 | atomic_dec_mb(&se_deve->pr_ref_count); |
1421 | smp_mb__after_atomic(); | ||
1422 | } | 1408 | } |
1423 | 1409 | ||
1424 | static sense_reason_t | 1410 | static sense_reason_t |
@@ -1551,15 +1537,13 @@ core_scsi3_decode_spec_i_port( | |||
1551 | if (!i_str) | 1537 | if (!i_str) |
1552 | continue; | 1538 | continue; |
1553 | 1539 | ||
1554 | atomic_inc(&tmp_tpg->tpg_pr_ref_count); | 1540 | atomic_inc_mb(&tmp_tpg->tpg_pr_ref_count); |
1555 | smp_mb__after_atomic(); | ||
1556 | spin_unlock(&dev->se_port_lock); | 1541 | spin_unlock(&dev->se_port_lock); |
1557 | 1542 | ||
1558 | if (core_scsi3_tpg_depend_item(tmp_tpg)) { | 1543 | if (core_scsi3_tpg_depend_item(tmp_tpg)) { |
1559 | pr_err(" core_scsi3_tpg_depend_item()" | 1544 | pr_err(" core_scsi3_tpg_depend_item()" |
1560 | " for tmp_tpg\n"); | 1545 | " for tmp_tpg\n"); |
1561 | atomic_dec(&tmp_tpg->tpg_pr_ref_count); | 1546 | atomic_dec_mb(&tmp_tpg->tpg_pr_ref_count); |
1562 | smp_mb__after_atomic(); | ||
1563 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | 1547 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
1564 | goto out_unmap; | 1548 | goto out_unmap; |
1565 | } | 1549 | } |
@@ -1571,10 +1555,8 @@ core_scsi3_decode_spec_i_port( | |||
1571 | spin_lock_irq(&tmp_tpg->acl_node_lock); | 1555 | spin_lock_irq(&tmp_tpg->acl_node_lock); |
1572 | dest_node_acl = __core_tpg_get_initiator_node_acl( | 1556 | dest_node_acl = __core_tpg_get_initiator_node_acl( |
1573 | tmp_tpg, i_str); | 1557 | tmp_tpg, i_str); |
1574 | if (dest_node_acl) { | 1558 | if (dest_node_acl) |
1575 | atomic_inc(&dest_node_acl->acl_pr_ref_count); | 1559 | atomic_inc_mb(&dest_node_acl->acl_pr_ref_count); |
1576 | smp_mb__after_atomic(); | ||
1577 | } | ||
1578 | spin_unlock_irq(&tmp_tpg->acl_node_lock); | 1560 | spin_unlock_irq(&tmp_tpg->acl_node_lock); |
1579 | 1561 | ||
1580 | if (!dest_node_acl) { | 1562 | if (!dest_node_acl) { |
@@ -1586,8 +1568,7 @@ core_scsi3_decode_spec_i_port( | |||
1586 | if (core_scsi3_nodeacl_depend_item(dest_node_acl)) { | 1568 | if (core_scsi3_nodeacl_depend_item(dest_node_acl)) { |
1587 | pr_err("configfs_depend_item() failed" | 1569 | pr_err("configfs_depend_item() failed" |
1588 | " for dest_node_acl->acl_group\n"); | 1570 | " for dest_node_acl->acl_group\n"); |
1589 | atomic_dec(&dest_node_acl->acl_pr_ref_count); | 1571 | atomic_dec_mb(&dest_node_acl->acl_pr_ref_count); |
1590 | smp_mb__after_atomic(); | ||
1591 | core_scsi3_tpg_undepend_item(tmp_tpg); | 1572 | core_scsi3_tpg_undepend_item(tmp_tpg); |
1592 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | 1573 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
1593 | goto out_unmap; | 1574 | goto out_unmap; |
@@ -1646,8 +1627,7 @@ core_scsi3_decode_spec_i_port( | |||
1646 | if (core_scsi3_lunacl_depend_item(dest_se_deve)) { | 1627 | if (core_scsi3_lunacl_depend_item(dest_se_deve)) { |
1647 | pr_err("core_scsi3_lunacl_depend_item()" | 1628 | pr_err("core_scsi3_lunacl_depend_item()" |
1648 | " failed\n"); | 1629 | " failed\n"); |
1649 | atomic_dec(&dest_se_deve->pr_ref_count); | 1630 | atomic_dec_mb(&dest_se_deve->pr_ref_count); |
1650 | smp_mb__after_atomic(); | ||
1651 | core_scsi3_nodeacl_undepend_item(dest_node_acl); | 1631 | core_scsi3_nodeacl_undepend_item(dest_node_acl); |
1652 | core_scsi3_tpg_undepend_item(dest_tpg); | 1632 | core_scsi3_tpg_undepend_item(dest_tpg); |
1653 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | 1633 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
@@ -3167,15 +3147,13 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, | |||
3167 | if (!dest_tf_ops) | 3147 | if (!dest_tf_ops) |
3168 | continue; | 3148 | continue; |
3169 | 3149 | ||
3170 | atomic_inc(&dest_se_tpg->tpg_pr_ref_count); | 3150 | atomic_inc_mb(&dest_se_tpg->tpg_pr_ref_count); |
3171 | smp_mb__after_atomic(); | ||
3172 | spin_unlock(&dev->se_port_lock); | 3151 | spin_unlock(&dev->se_port_lock); |
3173 | 3152 | ||
3174 | if (core_scsi3_tpg_depend_item(dest_se_tpg)) { | 3153 | if (core_scsi3_tpg_depend_item(dest_se_tpg)) { |
3175 | pr_err("core_scsi3_tpg_depend_item() failed" | 3154 | pr_err("core_scsi3_tpg_depend_item() failed" |
3176 | " for dest_se_tpg\n"); | 3155 | " for dest_se_tpg\n"); |
3177 | atomic_dec(&dest_se_tpg->tpg_pr_ref_count); | 3156 | atomic_dec_mb(&dest_se_tpg->tpg_pr_ref_count); |
3178 | smp_mb__after_atomic(); | ||
3179 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | 3157 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
3180 | goto out_put_pr_reg; | 3158 | goto out_put_pr_reg; |
3181 | } | 3159 | } |
@@ -3271,10 +3249,8 @@ after_iport_check: | |||
3271 | spin_lock_irq(&dest_se_tpg->acl_node_lock); | 3249 | spin_lock_irq(&dest_se_tpg->acl_node_lock); |
3272 | dest_node_acl = __core_tpg_get_initiator_node_acl(dest_se_tpg, | 3250 | dest_node_acl = __core_tpg_get_initiator_node_acl(dest_se_tpg, |
3273 | initiator_str); | 3251 | initiator_str); |
3274 | if (dest_node_acl) { | 3252 | if (dest_node_acl) |
3275 | atomic_inc(&dest_node_acl->acl_pr_ref_count); | 3253 | atomic_inc_mb(&dest_node_acl->acl_pr_ref_count); |
3276 | smp_mb__after_atomic(); | ||
3277 | } | ||
3278 | spin_unlock_irq(&dest_se_tpg->acl_node_lock); | 3254 | spin_unlock_irq(&dest_se_tpg->acl_node_lock); |
3279 | 3255 | ||
3280 | if (!dest_node_acl) { | 3256 | if (!dest_node_acl) { |
@@ -3288,8 +3264,7 @@ after_iport_check: | |||
3288 | if (core_scsi3_nodeacl_depend_item(dest_node_acl)) { | 3264 | if (core_scsi3_nodeacl_depend_item(dest_node_acl)) { |
3289 | pr_err("core_scsi3_nodeacl_depend_item() for" | 3265 | pr_err("core_scsi3_nodeacl_depend_item() for" |
3290 | " dest_node_acl\n"); | 3266 | " dest_node_acl\n"); |
3291 | atomic_dec(&dest_node_acl->acl_pr_ref_count); | 3267 | atomic_dec_mb(&dest_node_acl->acl_pr_ref_count); |
3292 | smp_mb__after_atomic(); | ||
3293 | dest_node_acl = NULL; | 3268 | dest_node_acl = NULL; |
3294 | ret = TCM_INVALID_PARAMETER_LIST; | 3269 | ret = TCM_INVALID_PARAMETER_LIST; |
3295 | goto out; | 3270 | goto out; |
@@ -3313,8 +3288,7 @@ after_iport_check: | |||
3313 | 3288 | ||
3314 | if (core_scsi3_lunacl_depend_item(dest_se_deve)) { | 3289 | if (core_scsi3_lunacl_depend_item(dest_se_deve)) { |
3315 | pr_err("core_scsi3_lunacl_depend_item() failed\n"); | 3290 | pr_err("core_scsi3_lunacl_depend_item() failed\n"); |
3316 | atomic_dec(&dest_se_deve->pr_ref_count); | 3291 | atomic_dec_mb(&dest_se_deve->pr_ref_count); |
3317 | smp_mb__after_atomic(); | ||
3318 | dest_se_deve = NULL; | 3292 | dest_se_deve = NULL; |
3319 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | 3293 | ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
3320 | goto out; | 3294 | goto out; |
@@ -3497,6 +3471,7 @@ static unsigned long long core_scsi3_extract_reservation_key(unsigned char *cdb) | |||
3497 | sense_reason_t | 3471 | sense_reason_t |
3498 | target_scsi3_emulate_pr_out(struct se_cmd *cmd) | 3472 | target_scsi3_emulate_pr_out(struct se_cmd *cmd) |
3499 | { | 3473 | { |
3474 | struct se_device *dev = cmd->se_dev; | ||
3500 | unsigned char *cdb = &cmd->t_task_cdb[0]; | 3475 | unsigned char *cdb = &cmd->t_task_cdb[0]; |
3501 | unsigned char *buf; | 3476 | unsigned char *buf; |
3502 | u64 res_key, sa_res_key; | 3477 | u64 res_key, sa_res_key; |
@@ -3561,6 +3536,13 @@ target_scsi3_emulate_pr_out(struct se_cmd *cmd) | |||
3561 | aptpl = (buf[17] & 0x01); | 3536 | aptpl = (buf[17] & 0x01); |
3562 | unreg = (buf[17] & 0x02); | 3537 | unreg = (buf[17] & 0x02); |
3563 | } | 3538 | } |
3539 | /* | ||
3540 | * If the backend device has been configured to force APTPL metadata | ||
3541 | * write-out, go ahead and propigate aptpl=1 down now. | ||
3542 | */ | ||
3543 | if (dev->dev_attrib.force_pr_aptpl) | ||
3544 | aptpl = 1; | ||
3545 | |||
3564 | transport_kunmap_data_sg(cmd); | 3546 | transport_kunmap_data_sg(cmd); |
3565 | buf = NULL; | 3547 | buf = NULL; |
3566 | 3548 | ||
@@ -3803,7 +3785,7 @@ core_scsi3_pri_report_capabilities(struct se_cmd *cmd) | |||
3803 | if (!buf) | 3785 | if (!buf) |
3804 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | 3786 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
3805 | 3787 | ||
3806 | buf[0] = ((add_len << 8) & 0xff); | 3788 | buf[0] = ((add_len >> 8) & 0xff); |
3807 | buf[1] = (add_len & 0xff); | 3789 | buf[1] = (add_len & 0xff); |
3808 | buf[2] |= 0x10; /* CRH: Compatible Reservation Hanlding bit. */ | 3790 | buf[2] |= 0x10; /* CRH: Compatible Reservation Hanlding bit. */ |
3809 | buf[2] |= 0x08; /* SIP_C: Specify Initiator Ports Capable bit */ | 3791 | buf[2] |= 0x08; /* SIP_C: Specify Initiator Ports Capable bit */ |
@@ -3879,8 +3861,7 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd) | |||
3879 | se_tpg = pr_reg->pr_reg_nacl->se_tpg; | 3861 | se_tpg = pr_reg->pr_reg_nacl->se_tpg; |
3880 | add_desc_len = 0; | 3862 | add_desc_len = 0; |
3881 | 3863 | ||
3882 | atomic_inc(&pr_reg->pr_res_holders); | 3864 | atomic_inc_mb(&pr_reg->pr_res_holders); |
3883 | smp_mb__after_atomic(); | ||
3884 | spin_unlock(&pr_tmpl->registration_lock); | 3865 | spin_unlock(&pr_tmpl->registration_lock); |
3885 | /* | 3866 | /* |
3886 | * Determine expected length of $FABRIC_MOD specific | 3867 | * Determine expected length of $FABRIC_MOD specific |
@@ -3893,8 +3874,7 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd) | |||
3893 | pr_warn("SPC-3 PRIN READ_FULL_STATUS ran" | 3874 | pr_warn("SPC-3 PRIN READ_FULL_STATUS ran" |
3894 | " out of buffer: %d\n", cmd->data_length); | 3875 | " out of buffer: %d\n", cmd->data_length); |
3895 | spin_lock(&pr_tmpl->registration_lock); | 3876 | spin_lock(&pr_tmpl->registration_lock); |
3896 | atomic_dec(&pr_reg->pr_res_holders); | 3877 | atomic_dec_mb(&pr_reg->pr_res_holders); |
3897 | smp_mb__after_atomic(); | ||
3898 | break; | 3878 | break; |
3899 | } | 3879 | } |
3900 | /* | 3880 | /* |
@@ -3955,8 +3935,7 @@ core_scsi3_pri_read_full_status(struct se_cmd *cmd) | |||
3955 | se_nacl, pr_reg, &format_code, &buf[off+4]); | 3935 | se_nacl, pr_reg, &format_code, &buf[off+4]); |
3956 | 3936 | ||
3957 | spin_lock(&pr_tmpl->registration_lock); | 3937 | spin_lock(&pr_tmpl->registration_lock); |
3958 | atomic_dec(&pr_reg->pr_res_holders); | 3938 | atomic_dec_mb(&pr_reg->pr_res_holders); |
3959 | smp_mb__after_atomic(); | ||
3960 | /* | 3939 | /* |
3961 | * Set the ADDITIONAL DESCRIPTOR LENGTH | 3940 | * Set the ADDITIONAL DESCRIPTOR LENGTH |
3962 | */ | 3941 | */ |
diff --git a/drivers/target/target_core_pr.h b/drivers/target/target_core_pr.h index 2ee2936fa0bd..749fd7bb7510 100644 --- a/drivers/target/target_core_pr.h +++ b/drivers/target/target_core_pr.h | |||
@@ -60,7 +60,7 @@ extern int core_scsi3_alloc_aptpl_registration( | |||
60 | unsigned char *, u16, u32, int, int, u8); | 60 | unsigned char *, u16, u32, int, int, u8); |
61 | extern int core_scsi3_check_aptpl_registration(struct se_device *, | 61 | extern int core_scsi3_check_aptpl_registration(struct se_device *, |
62 | struct se_portal_group *, struct se_lun *, | 62 | struct se_portal_group *, struct se_lun *, |
63 | struct se_lun_acl *); | 63 | struct se_node_acl *, u32); |
64 | extern void core_scsi3_free_pr_reg_from_nacl(struct se_device *, | 64 | extern void core_scsi3_free_pr_reg_from_nacl(struct se_device *, |
65 | struct se_node_acl *); | 65 | struct se_node_acl *); |
66 | extern void core_scsi3_free_all_registrations(struct se_device *); | 66 | extern void core_scsi3_free_all_registrations(struct se_device *); |
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 70d9f6dabba0..7c8291f0bbbc 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c | |||
@@ -749,14 +749,18 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_device *dev, | |||
749 | ret = -EINVAL; | 749 | ret = -EINVAL; |
750 | goto out; | 750 | goto out; |
751 | } | 751 | } |
752 | match_int(args, &arg); | 752 | ret = match_int(args, &arg); |
753 | if (ret) | ||
754 | goto out; | ||
753 | pdv->pdv_host_id = arg; | 755 | pdv->pdv_host_id = arg; |
754 | pr_debug("PSCSI[%d]: Referencing SCSI Host ID:" | 756 | pr_debug("PSCSI[%d]: Referencing SCSI Host ID:" |
755 | " %d\n", phv->phv_host_id, pdv->pdv_host_id); | 757 | " %d\n", phv->phv_host_id, pdv->pdv_host_id); |
756 | pdv->pdv_flags |= PDF_HAS_VIRT_HOST_ID; | 758 | pdv->pdv_flags |= PDF_HAS_VIRT_HOST_ID; |
757 | break; | 759 | break; |
758 | case Opt_scsi_channel_id: | 760 | case Opt_scsi_channel_id: |
759 | match_int(args, &arg); | 761 | ret = match_int(args, &arg); |
762 | if (ret) | ||
763 | goto out; | ||
760 | pdv->pdv_channel_id = arg; | 764 | pdv->pdv_channel_id = arg; |
761 | pr_debug("PSCSI[%d]: Referencing SCSI Channel" | 765 | pr_debug("PSCSI[%d]: Referencing SCSI Channel" |
762 | " ID: %d\n", phv->phv_host_id, | 766 | " ID: %d\n", phv->phv_host_id, |
@@ -764,7 +768,9 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_device *dev, | |||
764 | pdv->pdv_flags |= PDF_HAS_CHANNEL_ID; | 768 | pdv->pdv_flags |= PDF_HAS_CHANNEL_ID; |
765 | break; | 769 | break; |
766 | case Opt_scsi_target_id: | 770 | case Opt_scsi_target_id: |
767 | match_int(args, &arg); | 771 | ret = match_int(args, &arg); |
772 | if (ret) | ||
773 | goto out; | ||
768 | pdv->pdv_target_id = arg; | 774 | pdv->pdv_target_id = arg; |
769 | pr_debug("PSCSI[%d]: Referencing SCSI Target" | 775 | pr_debug("PSCSI[%d]: Referencing SCSI Target" |
770 | " ID: %d\n", phv->phv_host_id, | 776 | " ID: %d\n", phv->phv_host_id, |
@@ -772,7 +778,9 @@ static ssize_t pscsi_set_configfs_dev_params(struct se_device *dev, | |||
772 | pdv->pdv_flags |= PDF_HAS_TARGET_ID; | 778 | pdv->pdv_flags |= PDF_HAS_TARGET_ID; |
773 | break; | 779 | break; |
774 | case Opt_scsi_lun_id: | 780 | case Opt_scsi_lun_id: |
775 | match_int(args, &arg); | 781 | ret = match_int(args, &arg); |
782 | if (ret) | ||
783 | goto out; | ||
776 | pdv->pdv_lun_id = arg; | 784 | pdv->pdv_lun_id = arg; |
777 | pr_debug("PSCSI[%d]: Referencing SCSI LUN ID:" | 785 | pr_debug("PSCSI[%d]: Referencing SCSI LUN ID:" |
778 | " %d\n", phv->phv_host_id, pdv->pdv_lun_id); | 786 | " %d\n", phv->phv_host_id, pdv->pdv_lun_id); |
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index bd78d9235ac6..ebe62afb957d 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c | |||
@@ -948,7 +948,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) | |||
948 | } | 948 | } |
949 | 949 | ||
950 | /* reject any command that we don't have a handler for */ | 950 | /* reject any command that we don't have a handler for */ |
951 | if (!(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) && !cmd->execute_cmd) | 951 | if (!cmd->execute_cmd) |
952 | return TCM_UNSUPPORTED_SCSI_OPCODE; | 952 | return TCM_UNSUPPORTED_SCSI_OPCODE; |
953 | 953 | ||
954 | if (cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) { | 954 | if (cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) { |
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index f7cd95e8111a..fa5e157db47b 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c | |||
@@ -64,21 +64,17 @@ int core_tmr_alloc_req( | |||
64 | } | 64 | } |
65 | EXPORT_SYMBOL(core_tmr_alloc_req); | 65 | EXPORT_SYMBOL(core_tmr_alloc_req); |
66 | 66 | ||
67 | void core_tmr_release_req( | 67 | void core_tmr_release_req(struct se_tmr_req *tmr) |
68 | struct se_tmr_req *tmr) | ||
69 | { | 68 | { |
70 | struct se_device *dev = tmr->tmr_dev; | 69 | struct se_device *dev = tmr->tmr_dev; |
71 | unsigned long flags; | 70 | unsigned long flags; |
72 | 71 | ||
73 | if (!dev) { | 72 | if (dev) { |
74 | kfree(tmr); | 73 | spin_lock_irqsave(&dev->se_tmr_lock, flags); |
75 | return; | 74 | list_del(&tmr->tmr_list); |
75 | spin_unlock_irqrestore(&dev->se_tmr_lock, flags); | ||
76 | } | 76 | } |
77 | 77 | ||
78 | spin_lock_irqsave(&dev->se_tmr_lock, flags); | ||
79 | list_del(&tmr->tmr_list); | ||
80 | spin_unlock_irqrestore(&dev->se_tmr_lock, flags); | ||
81 | |||
82 | kfree(tmr); | 78 | kfree(tmr); |
83 | } | 79 | } |
84 | 80 | ||
@@ -90,9 +86,8 @@ static void core_tmr_handle_tas_abort( | |||
90 | bool remove = true; | 86 | bool remove = true; |
91 | /* | 87 | /* |
92 | * TASK ABORTED status (TAS) bit support | 88 | * TASK ABORTED status (TAS) bit support |
93 | */ | 89 | */ |
94 | if ((tmr_nacl && | 90 | if ((tmr_nacl && (tmr_nacl != cmd->se_sess->se_node_acl)) && tas) { |
95 | (tmr_nacl != cmd->se_sess->se_node_acl)) && tas) { | ||
96 | remove = false; | 91 | remove = false; |
97 | transport_send_task_abort(cmd); | 92 | transport_send_task_abort(cmd); |
98 | } | 93 | } |
@@ -120,13 +115,12 @@ void core_tmr_abort_task( | |||
120 | struct se_tmr_req *tmr, | 115 | struct se_tmr_req *tmr, |
121 | struct se_session *se_sess) | 116 | struct se_session *se_sess) |
122 | { | 117 | { |
123 | struct se_cmd *se_cmd, *tmp_cmd; | 118 | struct se_cmd *se_cmd; |
124 | unsigned long flags; | 119 | unsigned long flags; |
125 | int ref_tag; | 120 | int ref_tag; |
126 | 121 | ||
127 | spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); | 122 | spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); |
128 | list_for_each_entry_safe(se_cmd, tmp_cmd, | 123 | list_for_each_entry(se_cmd, &se_sess->sess_cmd_list, se_cmd_list) { |
129 | &se_sess->sess_cmd_list, se_cmd_list) { | ||
130 | 124 | ||
131 | if (dev != se_cmd->se_dev) | 125 | if (dev != se_cmd->se_dev) |
132 | continue; | 126 | continue; |
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index be783f717f19..0696de9553d3 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <target/target_core_fabric.h> | 40 | #include <target/target_core_fabric.h> |
41 | 41 | ||
42 | #include "target_core_internal.h" | 42 | #include "target_core_internal.h" |
43 | #include "target_core_pr.h" | ||
43 | 44 | ||
44 | extern struct se_device *g_lun0_dev; | 45 | extern struct se_device *g_lun0_dev; |
45 | 46 | ||
@@ -166,6 +167,13 @@ void core_tpg_add_node_to_devs( | |||
166 | 167 | ||
167 | core_enable_device_list_for_node(lun, NULL, lun->unpacked_lun, | 168 | core_enable_device_list_for_node(lun, NULL, lun->unpacked_lun, |
168 | lun_access, acl, tpg); | 169 | lun_access, acl, tpg); |
170 | /* | ||
171 | * Check to see if there are any existing persistent reservation | ||
172 | * APTPL pre-registrations that need to be enabled for this dynamic | ||
173 | * LUN ACL now.. | ||
174 | */ | ||
175 | core_scsi3_check_aptpl_registration(dev, tpg, lun, acl, | ||
176 | lun->unpacked_lun); | ||
169 | spin_lock(&tpg->tpg_lun_lock); | 177 | spin_lock(&tpg->tpg_lun_lock); |
170 | } | 178 | } |
171 | spin_unlock(&tpg->tpg_lun_lock); | 179 | spin_unlock(&tpg->tpg_lun_lock); |
@@ -335,7 +343,7 @@ void core_tpg_clear_object_luns(struct se_portal_group *tpg) | |||
335 | continue; | 343 | continue; |
336 | 344 | ||
337 | spin_unlock(&tpg->tpg_lun_lock); | 345 | spin_unlock(&tpg->tpg_lun_lock); |
338 | core_dev_del_lun(tpg, lun->unpacked_lun); | 346 | core_dev_del_lun(tpg, lun); |
339 | spin_lock(&tpg->tpg_lun_lock); | 347 | spin_lock(&tpg->tpg_lun_lock); |
340 | } | 348 | } |
341 | spin_unlock(&tpg->tpg_lun_lock); | 349 | spin_unlock(&tpg->tpg_lun_lock); |
@@ -663,13 +671,6 @@ static int core_tpg_setup_virtual_lun0(struct se_portal_group *se_tpg) | |||
663 | return 0; | 671 | return 0; |
664 | } | 672 | } |
665 | 673 | ||
666 | static void core_tpg_release_virtual_lun0(struct se_portal_group *se_tpg) | ||
667 | { | ||
668 | struct se_lun *lun = &se_tpg->tpg_virt_lun0; | ||
669 | |||
670 | core_tpg_post_dellun(se_tpg, lun); | ||
671 | } | ||
672 | |||
673 | int core_tpg_register( | 674 | int core_tpg_register( |
674 | struct target_core_fabric_ops *tfo, | 675 | struct target_core_fabric_ops *tfo, |
675 | struct se_wwn *se_wwn, | 676 | struct se_wwn *se_wwn, |
@@ -773,7 +774,7 @@ int core_tpg_deregister(struct se_portal_group *se_tpg) | |||
773 | spin_unlock_irq(&se_tpg->acl_node_lock); | 774 | spin_unlock_irq(&se_tpg->acl_node_lock); |
774 | 775 | ||
775 | if (se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) | 776 | if (se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) |
776 | core_tpg_release_virtual_lun0(se_tpg); | 777 | core_tpg_remove_lun(se_tpg, &se_tpg->tpg_virt_lun0); |
777 | 778 | ||
778 | se_tpg->se_tpg_fabric_ptr = NULL; | 779 | se_tpg->se_tpg_fabric_ptr = NULL; |
779 | array_free(se_tpg->tpg_lun_list, TRANSPORT_MAX_LUNS_PER_TPG); | 780 | array_free(se_tpg->tpg_lun_list, TRANSPORT_MAX_LUNS_PER_TPG); |
@@ -838,37 +839,7 @@ int core_tpg_add_lun( | |||
838 | return 0; | 839 | return 0; |
839 | } | 840 | } |
840 | 841 | ||
841 | struct se_lun *core_tpg_pre_dellun( | 842 | void core_tpg_remove_lun( |
842 | struct se_portal_group *tpg, | ||
843 | u32 unpacked_lun) | ||
844 | { | ||
845 | struct se_lun *lun; | ||
846 | |||
847 | if (unpacked_lun > (TRANSPORT_MAX_LUNS_PER_TPG-1)) { | ||
848 | pr_err("%s LUN: %u exceeds TRANSPORT_MAX_LUNS_PER_TPG" | ||
849 | "-1: %u for Target Portal Group: %u\n", | ||
850 | tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun, | ||
851 | TRANSPORT_MAX_LUNS_PER_TPG-1, | ||
852 | tpg->se_tpg_tfo->tpg_get_tag(tpg)); | ||
853 | return ERR_PTR(-EOVERFLOW); | ||
854 | } | ||
855 | |||
856 | spin_lock(&tpg->tpg_lun_lock); | ||
857 | lun = tpg->tpg_lun_list[unpacked_lun]; | ||
858 | if (lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE) { | ||
859 | pr_err("%s Logical Unit Number: %u is not active on" | ||
860 | " Target Portal Group: %u, ignoring request.\n", | ||
861 | tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun, | ||
862 | tpg->se_tpg_tfo->tpg_get_tag(tpg)); | ||
863 | spin_unlock(&tpg->tpg_lun_lock); | ||
864 | return ERR_PTR(-ENODEV); | ||
865 | } | ||
866 | spin_unlock(&tpg->tpg_lun_lock); | ||
867 | |||
868 | return lun; | ||
869 | } | ||
870 | |||
871 | int core_tpg_post_dellun( | ||
872 | struct se_portal_group *tpg, | 843 | struct se_portal_group *tpg, |
873 | struct se_lun *lun) | 844 | struct se_lun *lun) |
874 | { | 845 | { |
@@ -882,6 +853,4 @@ int core_tpg_post_dellun( | |||
882 | spin_unlock(&tpg->tpg_lun_lock); | 853 | spin_unlock(&tpg->tpg_lun_lock); |
883 | 854 | ||
884 | percpu_ref_exit(&lun->lun_ref); | 855 | percpu_ref_exit(&lun->lun_ref); |
885 | |||
886 | return 0; | ||
887 | } | 856 | } |
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 7fa62fc93e0b..9ea0d5f03f7a 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c | |||
@@ -232,6 +232,10 @@ void transport_subsystem_check_init(void) | |||
232 | if (ret != 0) | 232 | if (ret != 0) |
233 | pr_err("Unable to load target_core_pscsi\n"); | 233 | pr_err("Unable to load target_core_pscsi\n"); |
234 | 234 | ||
235 | ret = request_module("target_core_user"); | ||
236 | if (ret != 0) | ||
237 | pr_err("Unable to load target_core_user\n"); | ||
238 | |||
235 | sub_api_initialized = 1; | 239 | sub_api_initialized = 1; |
236 | } | 240 | } |
237 | 241 | ||
@@ -752,8 +756,7 @@ void target_qf_do_work(struct work_struct *work) | |||
752 | 756 | ||
753 | list_for_each_entry_safe(cmd, cmd_tmp, &qf_cmd_list, se_qf_node) { | 757 | list_for_each_entry_safe(cmd, cmd_tmp, &qf_cmd_list, se_qf_node) { |
754 | list_del(&cmd->se_qf_node); | 758 | list_del(&cmd->se_qf_node); |
755 | atomic_dec(&dev->dev_qf_count); | 759 | atomic_dec_mb(&dev->dev_qf_count); |
756 | smp_mb__after_atomic(); | ||
757 | 760 | ||
758 | pr_debug("Processing %s cmd: %p QUEUE_FULL in work queue" | 761 | pr_debug("Processing %s cmd: %p QUEUE_FULL in work queue" |
759 | " context: %s\n", cmd->se_tfo->get_fabric_name(), cmd, | 762 | " context: %s\n", cmd->se_tfo->get_fabric_name(), cmd, |
@@ -1166,7 +1169,6 @@ transport_check_alloc_task_attr(struct se_cmd *cmd) | |||
1166 | * Dormant to Active status. | 1169 | * Dormant to Active status. |
1167 | */ | 1170 | */ |
1168 | cmd->se_ordered_id = atomic_inc_return(&dev->dev_ordered_id); | 1171 | cmd->se_ordered_id = atomic_inc_return(&dev->dev_ordered_id); |
1169 | smp_mb__after_atomic(); | ||
1170 | pr_debug("Allocated se_ordered_id: %u for Task Attr: 0x%02x on %s\n", | 1172 | pr_debug("Allocated se_ordered_id: %u for Task Attr: 0x%02x on %s\n", |
1171 | cmd->se_ordered_id, cmd->sam_task_attr, | 1173 | cmd->se_ordered_id, cmd->sam_task_attr, |
1172 | dev->transport->name); | 1174 | dev->transport->name); |
@@ -1722,8 +1724,7 @@ static bool target_handle_task_attr(struct se_cmd *cmd) | |||
1722 | cmd->t_task_cdb[0], cmd->se_ordered_id); | 1724 | cmd->t_task_cdb[0], cmd->se_ordered_id); |
1723 | return false; | 1725 | return false; |
1724 | case MSG_ORDERED_TAG: | 1726 | case MSG_ORDERED_TAG: |
1725 | atomic_inc(&dev->dev_ordered_sync); | 1727 | atomic_inc_mb(&dev->dev_ordered_sync); |
1726 | smp_mb__after_atomic(); | ||
1727 | 1728 | ||
1728 | pr_debug("Added ORDERED for CDB: 0x%02x to ordered list, " | 1729 | pr_debug("Added ORDERED for CDB: 0x%02x to ordered list, " |
1729 | " se_ordered_id: %u\n", | 1730 | " se_ordered_id: %u\n", |
@@ -1740,8 +1741,7 @@ static bool target_handle_task_attr(struct se_cmd *cmd) | |||
1740 | /* | 1741 | /* |
1741 | * For SIMPLE and UNTAGGED Task Attribute commands | 1742 | * For SIMPLE and UNTAGGED Task Attribute commands |
1742 | */ | 1743 | */ |
1743 | atomic_inc(&dev->simple_cmds); | 1744 | atomic_inc_mb(&dev->simple_cmds); |
1744 | smp_mb__after_atomic(); | ||
1745 | break; | 1745 | break; |
1746 | } | 1746 | } |
1747 | 1747 | ||
@@ -1845,8 +1845,7 @@ static void transport_complete_task_attr(struct se_cmd *cmd) | |||
1845 | return; | 1845 | return; |
1846 | 1846 | ||
1847 | if (cmd->sam_task_attr == MSG_SIMPLE_TAG) { | 1847 | if (cmd->sam_task_attr == MSG_SIMPLE_TAG) { |
1848 | atomic_dec(&dev->simple_cmds); | 1848 | atomic_dec_mb(&dev->simple_cmds); |
1849 | smp_mb__after_atomic(); | ||
1850 | dev->dev_cur_ordered_id++; | 1849 | dev->dev_cur_ordered_id++; |
1851 | pr_debug("Incremented dev->dev_cur_ordered_id: %u for" | 1850 | pr_debug("Incremented dev->dev_cur_ordered_id: %u for" |
1852 | " SIMPLE: %u\n", dev->dev_cur_ordered_id, | 1851 | " SIMPLE: %u\n", dev->dev_cur_ordered_id, |
@@ -1857,8 +1856,7 @@ static void transport_complete_task_attr(struct se_cmd *cmd) | |||
1857 | " HEAD_OF_QUEUE: %u\n", dev->dev_cur_ordered_id, | 1856 | " HEAD_OF_QUEUE: %u\n", dev->dev_cur_ordered_id, |
1858 | cmd->se_ordered_id); | 1857 | cmd->se_ordered_id); |
1859 | } else if (cmd->sam_task_attr == MSG_ORDERED_TAG) { | 1858 | } else if (cmd->sam_task_attr == MSG_ORDERED_TAG) { |
1860 | atomic_dec(&dev->dev_ordered_sync); | 1859 | atomic_dec_mb(&dev->dev_ordered_sync); |
1861 | smp_mb__after_atomic(); | ||
1862 | 1860 | ||
1863 | dev->dev_cur_ordered_id++; | 1861 | dev->dev_cur_ordered_id++; |
1864 | pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED:" | 1862 | pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED:" |
@@ -1877,8 +1875,7 @@ static void transport_complete_qf(struct se_cmd *cmd) | |||
1877 | if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { | 1875 | if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { |
1878 | trace_target_cmd_complete(cmd); | 1876 | trace_target_cmd_complete(cmd); |
1879 | ret = cmd->se_tfo->queue_status(cmd); | 1877 | ret = cmd->se_tfo->queue_status(cmd); |
1880 | if (ret) | 1878 | goto out; |
1881 | goto out; | ||
1882 | } | 1879 | } |
1883 | 1880 | ||
1884 | switch (cmd->data_direction) { | 1881 | switch (cmd->data_direction) { |
@@ -1916,8 +1913,7 @@ static void transport_handle_queue_full( | |||
1916 | { | 1913 | { |
1917 | spin_lock_irq(&dev->qf_cmd_lock); | 1914 | spin_lock_irq(&dev->qf_cmd_lock); |
1918 | list_add_tail(&cmd->se_qf_node, &cmd->se_dev->qf_cmd_list); | 1915 | list_add_tail(&cmd->se_qf_node, &cmd->se_dev->qf_cmd_list); |
1919 | atomic_inc(&dev->dev_qf_count); | 1916 | atomic_inc_mb(&dev->dev_qf_count); |
1920 | smp_mb__after_atomic(); | ||
1921 | spin_unlock_irq(&cmd->se_dev->qf_cmd_lock); | 1917 | spin_unlock_irq(&cmd->se_dev->qf_cmd_lock); |
1922 | 1918 | ||
1923 | schedule_work(&cmd->se_dev->qf_work_queue); | 1919 | schedule_work(&cmd->se_dev->qf_work_queue); |
@@ -2896,7 +2892,6 @@ void transport_send_task_abort(struct se_cmd *cmd) | |||
2896 | if (cmd->se_tfo->write_pending_status(cmd) != 0) { | 2892 | if (cmd->se_tfo->write_pending_status(cmd) != 0) { |
2897 | cmd->transport_state |= CMD_T_ABORTED; | 2893 | cmd->transport_state |= CMD_T_ABORTED; |
2898 | cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS; | 2894 | cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS; |
2899 | smp_mb__after_atomic(); | ||
2900 | return; | 2895 | return; |
2901 | } | 2896 | } |
2902 | } | 2897 | } |
diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c index 101858e245b3..1738b1646988 100644 --- a/drivers/target/target_core_ua.c +++ b/drivers/target/target_core_ua.c | |||
@@ -161,8 +161,7 @@ int core_scsi3_ua_allocate( | |||
161 | spin_unlock(&deve->ua_lock); | 161 | spin_unlock(&deve->ua_lock); |
162 | spin_unlock_irq(&nacl->device_list_lock); | 162 | spin_unlock_irq(&nacl->device_list_lock); |
163 | 163 | ||
164 | atomic_inc(&deve->ua_count); | 164 | atomic_inc_mb(&deve->ua_count); |
165 | smp_mb__after_atomic(); | ||
166 | return 0; | 165 | return 0; |
167 | } | 166 | } |
168 | list_add_tail(&ua->ua_nacl_list, &deve->ua_list); | 167 | list_add_tail(&ua->ua_nacl_list, &deve->ua_list); |
@@ -174,8 +173,7 @@ int core_scsi3_ua_allocate( | |||
174 | nacl->se_tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun, | 173 | nacl->se_tpg->se_tpg_tfo->get_fabric_name(), unpacked_lun, |
175 | asc, ascq); | 174 | asc, ascq); |
176 | 175 | ||
177 | atomic_inc(&deve->ua_count); | 176 | atomic_inc_mb(&deve->ua_count); |
178 | smp_mb__after_atomic(); | ||
179 | return 0; | 177 | return 0; |
180 | } | 178 | } |
181 | 179 | ||
@@ -189,8 +187,7 @@ void core_scsi3_ua_release_all( | |||
189 | list_del(&ua->ua_nacl_list); | 187 | list_del(&ua->ua_nacl_list); |
190 | kmem_cache_free(se_ua_cache, ua); | 188 | kmem_cache_free(se_ua_cache, ua); |
191 | 189 | ||
192 | atomic_dec(&deve->ua_count); | 190 | atomic_dec_mb(&deve->ua_count); |
193 | smp_mb__after_atomic(); | ||
194 | } | 191 | } |
195 | spin_unlock(&deve->ua_lock); | 192 | spin_unlock(&deve->ua_lock); |
196 | } | 193 | } |
@@ -250,8 +247,7 @@ void core_scsi3_ua_for_check_condition( | |||
250 | list_del(&ua->ua_nacl_list); | 247 | list_del(&ua->ua_nacl_list); |
251 | kmem_cache_free(se_ua_cache, ua); | 248 | kmem_cache_free(se_ua_cache, ua); |
252 | 249 | ||
253 | atomic_dec(&deve->ua_count); | 250 | atomic_dec_mb(&deve->ua_count); |
254 | smp_mb__after_atomic(); | ||
255 | } | 251 | } |
256 | spin_unlock(&deve->ua_lock); | 252 | spin_unlock(&deve->ua_lock); |
257 | spin_unlock_irq(&nacl->device_list_lock); | 253 | spin_unlock_irq(&nacl->device_list_lock); |
@@ -309,8 +305,7 @@ int core_scsi3_ua_clear_for_request_sense( | |||
309 | list_del(&ua->ua_nacl_list); | 305 | list_del(&ua->ua_nacl_list); |
310 | kmem_cache_free(se_ua_cache, ua); | 306 | kmem_cache_free(se_ua_cache, ua); |
311 | 307 | ||
312 | atomic_dec(&deve->ua_count); | 308 | atomic_dec_mb(&deve->ua_count); |
313 | smp_mb__after_atomic(); | ||
314 | } | 309 | } |
315 | spin_unlock(&deve->ua_lock); | 310 | spin_unlock(&deve->ua_lock); |
316 | spin_unlock_irq(&nacl->device_list_lock); | 311 | spin_unlock_irq(&nacl->device_list_lock); |
diff --git a/drivers/target/target_core_ua.h b/drivers/target/target_core_ua.h index be912b36daae..a6b56b364e7a 100644 --- a/drivers/target/target_core_ua.h +++ b/drivers/target/target_core_ua.h | |||
@@ -1,4 +1,5 @@ | |||
1 | #ifndef TARGET_CORE_UA_H | 1 | #ifndef TARGET_CORE_UA_H |
2 | #define TARGET_CORE_UA_H | ||
2 | 3 | ||
3 | /* | 4 | /* |
4 | * From spc4r17, Table D.1: ASC and ASCQ Assignement | 5 | * From spc4r17, Table D.1: ASC and ASCQ Assignement |
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c new file mode 100644 index 000000000000..9a1b314f6482 --- /dev/null +++ b/drivers/target/target_core_user.c | |||
@@ -0,0 +1,1167 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Shaohua Li <shli@kernel.org> | ||
3 | * Copyright (C) 2014 Red Hat, Inc. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | */ | ||
18 | |||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/idr.h> | ||
22 | #include <linux/timer.h> | ||
23 | #include <linux/parser.h> | ||
24 | #include <scsi/scsi.h> | ||
25 | #include <scsi/scsi_host.h> | ||
26 | #include <linux/uio_driver.h> | ||
27 | #include <net/genetlink.h> | ||
28 | #include <target/target_core_base.h> | ||
29 | #include <target/target_core_fabric.h> | ||
30 | #include <target/target_core_backend.h> | ||
31 | #include <linux/target_core_user.h> | ||
32 | |||
33 | /* | ||
34 | * Define a shared-memory interface for LIO to pass SCSI commands and | ||
35 | * data to userspace for processing. This is to allow backends that | ||
36 | * are too complex for in-kernel support to be possible. | ||
37 | * | ||
38 | * It uses the UIO framework to do a lot of the device-creation and | ||
39 | * introspection work for us. | ||
40 | * | ||
41 | * See the .h file for how the ring is laid out. Note that while the | ||
42 | * command ring is defined, the particulars of the data area are | ||
43 | * not. Offset values in the command entry point to other locations | ||
44 | * internal to the mmap()ed area. There is separate space outside the | ||
45 | * command ring for data buffers. This leaves maximum flexibility for | ||
46 | * moving buffer allocations, or even page flipping or other | ||
47 | * allocation techniques, without altering the command ring layout. | ||
48 | * | ||
49 | * SECURITY: | ||
50 | * The user process must be assumed to be malicious. There's no way to | ||
51 | * prevent it breaking the command ring protocol if it wants, but in | ||
52 | * order to prevent other issues we must only ever read *data* from | ||
53 | * the shared memory area, not offsets or sizes. This applies to | ||
54 | * command ring entries as well as the mailbox. Extra code needed for | ||
55 | * this may have a 'UAM' comment. | ||
56 | */ | ||
57 | |||
58 | |||
59 | #define TCMU_TIME_OUT (30 * MSEC_PER_SEC) | ||
60 | |||
61 | #define CMDR_SIZE (16 * 4096) | ||
62 | #define DATA_SIZE (257 * 4096) | ||
63 | |||
64 | #define TCMU_RING_SIZE (CMDR_SIZE + DATA_SIZE) | ||
65 | |||
66 | static struct device *tcmu_root_device; | ||
67 | |||
68 | struct tcmu_hba { | ||
69 | u32 host_id; | ||
70 | }; | ||
71 | |||
72 | /* User wants all cmds or just some */ | ||
73 | enum passthru_level { | ||
74 | TCMU_PASS_ALL = 0, | ||
75 | TCMU_PASS_IO, | ||
76 | TCMU_PASS_INVALID, | ||
77 | }; | ||
78 | |||
79 | #define TCMU_CONFIG_LEN 256 | ||
80 | |||
81 | struct tcmu_dev { | ||
82 | struct se_device se_dev; | ||
83 | |||
84 | char *name; | ||
85 | struct se_hba *hba; | ||
86 | |||
87 | #define TCMU_DEV_BIT_OPEN 0 | ||
88 | #define TCMU_DEV_BIT_BROKEN 1 | ||
89 | unsigned long flags; | ||
90 | enum passthru_level pass_level; | ||
91 | |||
92 | struct uio_info uio_info; | ||
93 | |||
94 | struct tcmu_mailbox *mb_addr; | ||
95 | size_t dev_size; | ||
96 | u32 cmdr_size; | ||
97 | u32 cmdr_last_cleaned; | ||
98 | /* Offset of data ring from start of mb */ | ||
99 | size_t data_off; | ||
100 | size_t data_size; | ||
101 | /* Ring head + tail values. */ | ||
102 | /* Must add data_off and mb_addr to get the address */ | ||
103 | size_t data_head; | ||
104 | size_t data_tail; | ||
105 | |||
106 | wait_queue_head_t wait_cmdr; | ||
107 | /* TODO should this be a mutex? */ | ||
108 | spinlock_t cmdr_lock; | ||
109 | |||
110 | struct idr commands; | ||
111 | spinlock_t commands_lock; | ||
112 | |||
113 | struct timer_list timeout; | ||
114 | |||
115 | char dev_config[TCMU_CONFIG_LEN]; | ||
116 | }; | ||
117 | |||
118 | #define TCMU_DEV(_se_dev) container_of(_se_dev, struct tcmu_dev, se_dev) | ||
119 | |||
120 | #define CMDR_OFF sizeof(struct tcmu_mailbox) | ||
121 | |||
122 | struct tcmu_cmd { | ||
123 | struct se_cmd *se_cmd; | ||
124 | struct tcmu_dev *tcmu_dev; | ||
125 | |||
126 | uint16_t cmd_id; | ||
127 | |||
128 | /* Can't use se_cmd->data_length when cleaning up expired cmds, because if | ||
129 | cmd has been completed then accessing se_cmd is off limits */ | ||
130 | size_t data_length; | ||
131 | |||
132 | unsigned long deadline; | ||
133 | |||
134 | #define TCMU_CMD_BIT_EXPIRED 0 | ||
135 | unsigned long flags; | ||
136 | }; | ||
137 | |||
138 | static struct kmem_cache *tcmu_cmd_cache; | ||
139 | |||
140 | /* multicast group */ | ||
141 | enum tcmu_multicast_groups { | ||
142 | TCMU_MCGRP_CONFIG, | ||
143 | }; | ||
144 | |||
145 | static const struct genl_multicast_group tcmu_mcgrps[] = { | ||
146 | [TCMU_MCGRP_CONFIG] = { .name = "config", }, | ||
147 | }; | ||
148 | |||
149 | /* Our generic netlink family */ | ||
150 | static struct genl_family tcmu_genl_family = { | ||
151 | .id = GENL_ID_GENERATE, | ||
152 | .hdrsize = 0, | ||
153 | .name = "TCM-USER", | ||
154 | .version = 1, | ||
155 | .maxattr = TCMU_ATTR_MAX, | ||
156 | .mcgrps = tcmu_mcgrps, | ||
157 | .n_mcgrps = ARRAY_SIZE(tcmu_mcgrps), | ||
158 | }; | ||
159 | |||
160 | static struct tcmu_cmd *tcmu_alloc_cmd(struct se_cmd *se_cmd) | ||
161 | { | ||
162 | struct se_device *se_dev = se_cmd->se_dev; | ||
163 | struct tcmu_dev *udev = TCMU_DEV(se_dev); | ||
164 | struct tcmu_cmd *tcmu_cmd; | ||
165 | int cmd_id; | ||
166 | |||
167 | tcmu_cmd = kmem_cache_zalloc(tcmu_cmd_cache, GFP_KERNEL); | ||
168 | if (!tcmu_cmd) | ||
169 | return NULL; | ||
170 | |||
171 | tcmu_cmd->se_cmd = se_cmd; | ||
172 | tcmu_cmd->tcmu_dev = udev; | ||
173 | tcmu_cmd->data_length = se_cmd->data_length; | ||
174 | |||
175 | tcmu_cmd->deadline = jiffies + msecs_to_jiffies(TCMU_TIME_OUT); | ||
176 | |||
177 | idr_preload(GFP_KERNEL); | ||
178 | spin_lock_irq(&udev->commands_lock); | ||
179 | cmd_id = idr_alloc(&udev->commands, tcmu_cmd, 0, | ||
180 | USHRT_MAX, GFP_NOWAIT); | ||
181 | spin_unlock_irq(&udev->commands_lock); | ||
182 | idr_preload_end(); | ||
183 | |||
184 | if (cmd_id < 0) { | ||
185 | kmem_cache_free(tcmu_cmd_cache, tcmu_cmd); | ||
186 | return NULL; | ||
187 | } | ||
188 | tcmu_cmd->cmd_id = cmd_id; | ||
189 | |||
190 | return tcmu_cmd; | ||
191 | } | ||
192 | |||
193 | static inline void tcmu_flush_dcache_range(void *vaddr, size_t size) | ||
194 | { | ||
195 | unsigned long offset = (unsigned long) vaddr & ~PAGE_MASK; | ||
196 | |||
197 | size = round_up(size+offset, PAGE_SIZE); | ||
198 | vaddr -= offset; | ||
199 | |||
200 | while (size) { | ||
201 | flush_dcache_page(virt_to_page(vaddr)); | ||
202 | size -= PAGE_SIZE; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * Some ring helper functions. We don't assume size is a power of 2 so | ||
208 | * we can't use circ_buf.h. | ||
209 | */ | ||
210 | static inline size_t spc_used(size_t head, size_t tail, size_t size) | ||
211 | { | ||
212 | int diff = head - tail; | ||
213 | |||
214 | if (diff >= 0) | ||
215 | return diff; | ||
216 | else | ||
217 | return size + diff; | ||
218 | } | ||
219 | |||
220 | static inline size_t spc_free(size_t head, size_t tail, size_t size) | ||
221 | { | ||
222 | /* Keep 1 byte unused or we can't tell full from empty */ | ||
223 | return (size - spc_used(head, tail, size) - 1); | ||
224 | } | ||
225 | |||
226 | static inline size_t head_to_end(size_t head, size_t size) | ||
227 | { | ||
228 | return size - head; | ||
229 | } | ||
230 | |||
231 | #define UPDATE_HEAD(head, used, size) smp_store_release(&head, ((head % size) + used) % size) | ||
232 | |||
233 | /* | ||
234 | * We can't queue a command until we have space available on the cmd ring *and* space | ||
235 | * space avail on the data ring. | ||
236 | * | ||
237 | * Called with ring lock held. | ||
238 | */ | ||
239 | static bool is_ring_space_avail(struct tcmu_dev *udev, size_t cmd_size, size_t data_needed) | ||
240 | { | ||
241 | struct tcmu_mailbox *mb = udev->mb_addr; | ||
242 | size_t space; | ||
243 | u32 cmd_head; | ||
244 | size_t cmd_needed; | ||
245 | |||
246 | tcmu_flush_dcache_range(mb, sizeof(*mb)); | ||
247 | |||
248 | cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ | ||
249 | |||
250 | /* | ||
251 | * If cmd end-of-ring space is too small then we need space for a NOP plus | ||
252 | * original cmd - cmds are internally contiguous. | ||
253 | */ | ||
254 | if (head_to_end(cmd_head, udev->cmdr_size) >= cmd_size) | ||
255 | cmd_needed = cmd_size; | ||
256 | else | ||
257 | cmd_needed = cmd_size + head_to_end(cmd_head, udev->cmdr_size); | ||
258 | |||
259 | space = spc_free(cmd_head, udev->cmdr_last_cleaned, udev->cmdr_size); | ||
260 | if (space < cmd_needed) { | ||
261 | pr_debug("no cmd space: %u %u %u\n", cmd_head, | ||
262 | udev->cmdr_last_cleaned, udev->cmdr_size); | ||
263 | return false; | ||
264 | } | ||
265 | |||
266 | space = spc_free(udev->data_head, udev->data_tail, udev->data_size); | ||
267 | if (space < data_needed) { | ||
268 | pr_debug("no data space: %zu %zu %zu\n", udev->data_head, | ||
269 | udev->data_tail, udev->data_size); | ||
270 | return false; | ||
271 | } | ||
272 | |||
273 | return true; | ||
274 | } | ||
275 | |||
276 | static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) | ||
277 | { | ||
278 | struct tcmu_dev *udev = tcmu_cmd->tcmu_dev; | ||
279 | struct se_cmd *se_cmd = tcmu_cmd->se_cmd; | ||
280 | size_t base_command_size, command_size; | ||
281 | struct tcmu_mailbox *mb; | ||
282 | struct tcmu_cmd_entry *entry; | ||
283 | int i; | ||
284 | struct scatterlist *sg; | ||
285 | struct iovec *iov; | ||
286 | int iov_cnt = 0; | ||
287 | uint32_t cmd_head; | ||
288 | uint64_t cdb_off; | ||
289 | |||
290 | if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) | ||
291 | return -EINVAL; | ||
292 | |||
293 | /* | ||
294 | * Must be a certain minimum size for response sense info, but | ||
295 | * also may be larger if the iov array is large. | ||
296 | * | ||
297 | * iovs = sgl_nents+1, for end-of-ring case, plus another 1 | ||
298 | * b/c size == offsetof one-past-element. | ||
299 | */ | ||
300 | base_command_size = max(offsetof(struct tcmu_cmd_entry, | ||
301 | req.iov[se_cmd->t_data_nents + 2]), | ||
302 | sizeof(struct tcmu_cmd_entry)); | ||
303 | command_size = base_command_size | ||
304 | + round_up(scsi_command_size(se_cmd->t_task_cdb), TCMU_OP_ALIGN_SIZE); | ||
305 | |||
306 | WARN_ON(command_size & (TCMU_OP_ALIGN_SIZE-1)); | ||
307 | |||
308 | spin_lock_irq(&udev->cmdr_lock); | ||
309 | |||
310 | mb = udev->mb_addr; | ||
311 | cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ | ||
312 | if ((command_size > (udev->cmdr_size / 2)) | ||
313 | || tcmu_cmd->data_length > (udev->data_size - 1)) | ||
314 | pr_warn("TCMU: Request of size %zu/%zu may be too big for %u/%zu " | ||
315 | "cmd/data ring buffers\n", command_size, tcmu_cmd->data_length, | ||
316 | udev->cmdr_size, udev->data_size); | ||
317 | |||
318 | while (!is_ring_space_avail(udev, command_size, tcmu_cmd->data_length)) { | ||
319 | int ret; | ||
320 | DEFINE_WAIT(__wait); | ||
321 | |||
322 | prepare_to_wait(&udev->wait_cmdr, &__wait, TASK_INTERRUPTIBLE); | ||
323 | |||
324 | pr_debug("sleeping for ring space\n"); | ||
325 | spin_unlock_irq(&udev->cmdr_lock); | ||
326 | ret = schedule_timeout(msecs_to_jiffies(TCMU_TIME_OUT)); | ||
327 | finish_wait(&udev->wait_cmdr, &__wait); | ||
328 | if (!ret) { | ||
329 | pr_warn("tcmu: command timed out\n"); | ||
330 | return -ETIMEDOUT; | ||
331 | } | ||
332 | |||
333 | spin_lock_irq(&udev->cmdr_lock); | ||
334 | |||
335 | /* We dropped cmdr_lock, cmd_head is stale */ | ||
336 | cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ | ||
337 | } | ||
338 | |||
339 | /* Insert a PAD if end-of-ring space is too small */ | ||
340 | if (head_to_end(cmd_head, udev->cmdr_size) < command_size) { | ||
341 | size_t pad_size = head_to_end(cmd_head, udev->cmdr_size); | ||
342 | |||
343 | entry = (void *) mb + CMDR_OFF + cmd_head; | ||
344 | tcmu_flush_dcache_range(entry, sizeof(*entry)); | ||
345 | tcmu_hdr_set_op(&entry->hdr, TCMU_OP_PAD); | ||
346 | tcmu_hdr_set_len(&entry->hdr, pad_size); | ||
347 | |||
348 | UPDATE_HEAD(mb->cmd_head, pad_size, udev->cmdr_size); | ||
349 | |||
350 | cmd_head = mb->cmd_head % udev->cmdr_size; /* UAM */ | ||
351 | WARN_ON(cmd_head != 0); | ||
352 | } | ||
353 | |||
354 | entry = (void *) mb + CMDR_OFF + cmd_head; | ||
355 | tcmu_flush_dcache_range(entry, sizeof(*entry)); | ||
356 | tcmu_hdr_set_op(&entry->hdr, TCMU_OP_CMD); | ||
357 | tcmu_hdr_set_len(&entry->hdr, command_size); | ||
358 | entry->cmd_id = tcmu_cmd->cmd_id; | ||
359 | |||
360 | /* | ||
361 | * Fix up iovecs, and handle if allocation in data ring wrapped. | ||
362 | */ | ||
363 | iov = &entry->req.iov[0]; | ||
364 | for_each_sg(se_cmd->t_data_sg, sg, se_cmd->t_data_nents, i) { | ||
365 | size_t copy_bytes = min((size_t)sg->length, | ||
366 | head_to_end(udev->data_head, udev->data_size)); | ||
367 | void *from = kmap_atomic(sg_page(sg)) + sg->offset; | ||
368 | void *to = (void *) mb + udev->data_off + udev->data_head; | ||
369 | |||
370 | if (tcmu_cmd->se_cmd->data_direction == DMA_TO_DEVICE) { | ||
371 | memcpy(to, from, copy_bytes); | ||
372 | tcmu_flush_dcache_range(to, copy_bytes); | ||
373 | } | ||
374 | |||
375 | /* Even iov_base is relative to mb_addr */ | ||
376 | iov->iov_len = copy_bytes; | ||
377 | iov->iov_base = (void *) udev->data_off + udev->data_head; | ||
378 | iov_cnt++; | ||
379 | iov++; | ||
380 | |||
381 | UPDATE_HEAD(udev->data_head, copy_bytes, udev->data_size); | ||
382 | |||
383 | /* Uh oh, we wrapped the buffer. Must split sg across 2 iovs. */ | ||
384 | if (sg->length != copy_bytes) { | ||
385 | from += copy_bytes; | ||
386 | copy_bytes = sg->length - copy_bytes; | ||
387 | |||
388 | iov->iov_len = copy_bytes; | ||
389 | iov->iov_base = (void *) udev->data_off + udev->data_head; | ||
390 | |||
391 | if (se_cmd->data_direction == DMA_TO_DEVICE) { | ||
392 | to = (void *) mb + udev->data_off + udev->data_head; | ||
393 | memcpy(to, from, copy_bytes); | ||
394 | tcmu_flush_dcache_range(to, copy_bytes); | ||
395 | } | ||
396 | |||
397 | iov_cnt++; | ||
398 | iov++; | ||
399 | |||
400 | UPDATE_HEAD(udev->data_head, copy_bytes, udev->data_size); | ||
401 | } | ||
402 | |||
403 | kunmap_atomic(from); | ||
404 | } | ||
405 | entry->req.iov_cnt = iov_cnt; | ||
406 | |||
407 | /* All offsets relative to mb_addr, not start of entry! */ | ||
408 | cdb_off = CMDR_OFF + cmd_head + base_command_size; | ||
409 | memcpy((void *) mb + cdb_off, se_cmd->t_task_cdb, scsi_command_size(se_cmd->t_task_cdb)); | ||
410 | entry->req.cdb_off = cdb_off; | ||
411 | tcmu_flush_dcache_range(entry, sizeof(*entry)); | ||
412 | |||
413 | UPDATE_HEAD(mb->cmd_head, command_size, udev->cmdr_size); | ||
414 | tcmu_flush_dcache_range(mb, sizeof(*mb)); | ||
415 | |||
416 | spin_unlock_irq(&udev->cmdr_lock); | ||
417 | |||
418 | /* TODO: only if FLUSH and FUA? */ | ||
419 | uio_event_notify(&udev->uio_info); | ||
420 | |||
421 | mod_timer(&udev->timeout, | ||
422 | round_jiffies_up(jiffies + msecs_to_jiffies(TCMU_TIME_OUT))); | ||
423 | |||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | static int tcmu_queue_cmd(struct se_cmd *se_cmd) | ||
428 | { | ||
429 | struct se_device *se_dev = se_cmd->se_dev; | ||
430 | struct tcmu_dev *udev = TCMU_DEV(se_dev); | ||
431 | struct tcmu_cmd *tcmu_cmd; | ||
432 | int ret; | ||
433 | |||
434 | tcmu_cmd = tcmu_alloc_cmd(se_cmd); | ||
435 | if (!tcmu_cmd) | ||
436 | return -ENOMEM; | ||
437 | |||
438 | ret = tcmu_queue_cmd_ring(tcmu_cmd); | ||
439 | if (ret < 0) { | ||
440 | pr_err("TCMU: Could not queue command\n"); | ||
441 | spin_lock_irq(&udev->commands_lock); | ||
442 | idr_remove(&udev->commands, tcmu_cmd->cmd_id); | ||
443 | spin_unlock_irq(&udev->commands_lock); | ||
444 | |||
445 | kmem_cache_free(tcmu_cmd_cache, tcmu_cmd); | ||
446 | } | ||
447 | |||
448 | return ret; | ||
449 | } | ||
450 | |||
451 | static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *entry) | ||
452 | { | ||
453 | struct se_cmd *se_cmd = cmd->se_cmd; | ||
454 | struct tcmu_dev *udev = cmd->tcmu_dev; | ||
455 | |||
456 | if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) { | ||
457 | /* cmd has been completed already from timeout, just reclaim data | ||
458 | ring space */ | ||
459 | UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); | ||
460 | return; | ||
461 | } | ||
462 | |||
463 | if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { | ||
464 | memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer, | ||
465 | se_cmd->scsi_sense_length); | ||
466 | |||
467 | UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); | ||
468 | } | ||
469 | else if (se_cmd->data_direction == DMA_FROM_DEVICE) { | ||
470 | struct scatterlist *sg; | ||
471 | int i; | ||
472 | |||
473 | /* It'd be easier to look at entry's iovec again, but UAM */ | ||
474 | for_each_sg(se_cmd->t_data_sg, sg, se_cmd->t_data_nents, i) { | ||
475 | size_t copy_bytes; | ||
476 | void *to; | ||
477 | void *from; | ||
478 | |||
479 | copy_bytes = min((size_t)sg->length, | ||
480 | head_to_end(udev->data_tail, udev->data_size)); | ||
481 | |||
482 | to = kmap_atomic(sg_page(sg)) + sg->offset; | ||
483 | WARN_ON(sg->length + sg->offset > PAGE_SIZE); | ||
484 | from = (void *) udev->mb_addr + udev->data_off + udev->data_tail; | ||
485 | tcmu_flush_dcache_range(from, copy_bytes); | ||
486 | memcpy(to, from, copy_bytes); | ||
487 | |||
488 | UPDATE_HEAD(udev->data_tail, copy_bytes, udev->data_size); | ||
489 | |||
490 | /* Uh oh, wrapped the data buffer for this sg's data */ | ||
491 | if (sg->length != copy_bytes) { | ||
492 | from = (void *) udev->mb_addr + udev->data_off + udev->data_tail; | ||
493 | WARN_ON(udev->data_tail); | ||
494 | to += copy_bytes; | ||
495 | copy_bytes = sg->length - copy_bytes; | ||
496 | tcmu_flush_dcache_range(from, copy_bytes); | ||
497 | memcpy(to, from, copy_bytes); | ||
498 | |||
499 | UPDATE_HEAD(udev->data_tail, copy_bytes, udev->data_size); | ||
500 | } | ||
501 | |||
502 | kunmap_atomic(to); | ||
503 | } | ||
504 | |||
505 | } else if (se_cmd->data_direction == DMA_TO_DEVICE) { | ||
506 | UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); | ||
507 | } else { | ||
508 | pr_warn("TCMU: data direction was %d!\n", se_cmd->data_direction); | ||
509 | } | ||
510 | |||
511 | target_complete_cmd(cmd->se_cmd, entry->rsp.scsi_status); | ||
512 | cmd->se_cmd = NULL; | ||
513 | |||
514 | kmem_cache_free(tcmu_cmd_cache, cmd); | ||
515 | } | ||
516 | |||
517 | static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) | ||
518 | { | ||
519 | struct tcmu_mailbox *mb; | ||
520 | LIST_HEAD(cpl_cmds); | ||
521 | unsigned long flags; | ||
522 | int handled = 0; | ||
523 | |||
524 | if (test_bit(TCMU_DEV_BIT_BROKEN, &udev->flags)) { | ||
525 | pr_err("ring broken, not handling completions\n"); | ||
526 | return 0; | ||
527 | } | ||
528 | |||
529 | spin_lock_irqsave(&udev->cmdr_lock, flags); | ||
530 | |||
531 | mb = udev->mb_addr; | ||
532 | tcmu_flush_dcache_range(mb, sizeof(*mb)); | ||
533 | |||
534 | while (udev->cmdr_last_cleaned != ACCESS_ONCE(mb->cmd_tail)) { | ||
535 | |||
536 | struct tcmu_cmd_entry *entry = (void *) mb + CMDR_OFF + udev->cmdr_last_cleaned; | ||
537 | struct tcmu_cmd *cmd; | ||
538 | |||
539 | tcmu_flush_dcache_range(entry, sizeof(*entry)); | ||
540 | |||
541 | if (tcmu_hdr_get_op(&entry->hdr) == TCMU_OP_PAD) { | ||
542 | UPDATE_HEAD(udev->cmdr_last_cleaned, tcmu_hdr_get_len(&entry->hdr), udev->cmdr_size); | ||
543 | continue; | ||
544 | } | ||
545 | WARN_ON(tcmu_hdr_get_op(&entry->hdr) != TCMU_OP_CMD); | ||
546 | |||
547 | spin_lock(&udev->commands_lock); | ||
548 | cmd = idr_find(&udev->commands, entry->cmd_id); | ||
549 | if (cmd) | ||
550 | idr_remove(&udev->commands, cmd->cmd_id); | ||
551 | spin_unlock(&udev->commands_lock); | ||
552 | |||
553 | if (!cmd) { | ||
554 | pr_err("cmd_id not found, ring is broken\n"); | ||
555 | set_bit(TCMU_DEV_BIT_BROKEN, &udev->flags); | ||
556 | break; | ||
557 | } | ||
558 | |||
559 | tcmu_handle_completion(cmd, entry); | ||
560 | |||
561 | UPDATE_HEAD(udev->cmdr_last_cleaned, tcmu_hdr_get_len(&entry->hdr), udev->cmdr_size); | ||
562 | |||
563 | handled++; | ||
564 | } | ||
565 | |||
566 | if (mb->cmd_tail == mb->cmd_head) | ||
567 | del_timer(&udev->timeout); /* no more pending cmds */ | ||
568 | |||
569 | spin_unlock_irqrestore(&udev->cmdr_lock, flags); | ||
570 | |||
571 | wake_up(&udev->wait_cmdr); | ||
572 | |||
573 | return handled; | ||
574 | } | ||
575 | |||
576 | static int tcmu_check_expired_cmd(int id, void *p, void *data) | ||
577 | { | ||
578 | struct tcmu_cmd *cmd = p; | ||
579 | |||
580 | if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) | ||
581 | return 0; | ||
582 | |||
583 | if (!time_after(cmd->deadline, jiffies)) | ||
584 | return 0; | ||
585 | |||
586 | set_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags); | ||
587 | target_complete_cmd(cmd->se_cmd, SAM_STAT_CHECK_CONDITION); | ||
588 | cmd->se_cmd = NULL; | ||
589 | |||
590 | kmem_cache_free(tcmu_cmd_cache, cmd); | ||
591 | |||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | static void tcmu_device_timedout(unsigned long data) | ||
596 | { | ||
597 | struct tcmu_dev *udev = (struct tcmu_dev *)data; | ||
598 | unsigned long flags; | ||
599 | int handled; | ||
600 | |||
601 | handled = tcmu_handle_completions(udev); | ||
602 | |||
603 | pr_warn("%d completions handled from timeout\n", handled); | ||
604 | |||
605 | spin_lock_irqsave(&udev->commands_lock, flags); | ||
606 | idr_for_each(&udev->commands, tcmu_check_expired_cmd, NULL); | ||
607 | spin_unlock_irqrestore(&udev->commands_lock, flags); | ||
608 | |||
609 | /* | ||
610 | * We don't need to wakeup threads on wait_cmdr since they have their | ||
611 | * own timeout. | ||
612 | */ | ||
613 | } | ||
614 | |||
615 | static int tcmu_attach_hba(struct se_hba *hba, u32 host_id) | ||
616 | { | ||
617 | struct tcmu_hba *tcmu_hba; | ||
618 | |||
619 | tcmu_hba = kzalloc(sizeof(struct tcmu_hba), GFP_KERNEL); | ||
620 | if (!tcmu_hba) | ||
621 | return -ENOMEM; | ||
622 | |||
623 | tcmu_hba->host_id = host_id; | ||
624 | hba->hba_ptr = tcmu_hba; | ||
625 | |||
626 | return 0; | ||
627 | } | ||
628 | |||
629 | static void tcmu_detach_hba(struct se_hba *hba) | ||
630 | { | ||
631 | kfree(hba->hba_ptr); | ||
632 | hba->hba_ptr = NULL; | ||
633 | } | ||
634 | |||
635 | static struct se_device *tcmu_alloc_device(struct se_hba *hba, const char *name) | ||
636 | { | ||
637 | struct tcmu_dev *udev; | ||
638 | |||
639 | udev = kzalloc(sizeof(struct tcmu_dev), GFP_KERNEL); | ||
640 | if (!udev) | ||
641 | return NULL; | ||
642 | |||
643 | udev->name = kstrdup(name, GFP_KERNEL); | ||
644 | if (!udev->name) { | ||
645 | kfree(udev); | ||
646 | return NULL; | ||
647 | } | ||
648 | |||
649 | udev->hba = hba; | ||
650 | |||
651 | init_waitqueue_head(&udev->wait_cmdr); | ||
652 | spin_lock_init(&udev->cmdr_lock); | ||
653 | |||
654 | idr_init(&udev->commands); | ||
655 | spin_lock_init(&udev->commands_lock); | ||
656 | |||
657 | setup_timer(&udev->timeout, tcmu_device_timedout, | ||
658 | (unsigned long)udev); | ||
659 | |||
660 | udev->pass_level = TCMU_PASS_ALL; | ||
661 | |||
662 | return &udev->se_dev; | ||
663 | } | ||
664 | |||
665 | static int tcmu_irqcontrol(struct uio_info *info, s32 irq_on) | ||
666 | { | ||
667 | struct tcmu_dev *tcmu_dev = container_of(info, struct tcmu_dev, uio_info); | ||
668 | |||
669 | tcmu_handle_completions(tcmu_dev); | ||
670 | |||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | /* | ||
675 | * mmap code from uio.c. Copied here because we want to hook mmap() | ||
676 | * and this stuff must come along. | ||
677 | */ | ||
678 | static int tcmu_find_mem_index(struct vm_area_struct *vma) | ||
679 | { | ||
680 | struct tcmu_dev *udev = vma->vm_private_data; | ||
681 | struct uio_info *info = &udev->uio_info; | ||
682 | |||
683 | if (vma->vm_pgoff < MAX_UIO_MAPS) { | ||
684 | if (info->mem[vma->vm_pgoff].size == 0) | ||
685 | return -1; | ||
686 | return (int)vma->vm_pgoff; | ||
687 | } | ||
688 | return -1; | ||
689 | } | ||
690 | |||
691 | static int tcmu_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
692 | { | ||
693 | struct tcmu_dev *udev = vma->vm_private_data; | ||
694 | struct uio_info *info = &udev->uio_info; | ||
695 | struct page *page; | ||
696 | unsigned long offset; | ||
697 | void *addr; | ||
698 | |||
699 | int mi = tcmu_find_mem_index(vma); | ||
700 | if (mi < 0) | ||
701 | return VM_FAULT_SIGBUS; | ||
702 | |||
703 | /* | ||
704 | * We need to subtract mi because userspace uses offset = N*PAGE_SIZE | ||
705 | * to use mem[N]. | ||
706 | */ | ||
707 | offset = (vmf->pgoff - mi) << PAGE_SHIFT; | ||
708 | |||
709 | addr = (void *)(unsigned long)info->mem[mi].addr + offset; | ||
710 | if (info->mem[mi].memtype == UIO_MEM_LOGICAL) | ||
711 | page = virt_to_page(addr); | ||
712 | else | ||
713 | page = vmalloc_to_page(addr); | ||
714 | get_page(page); | ||
715 | vmf->page = page; | ||
716 | return 0; | ||
717 | } | ||
718 | |||
719 | static const struct vm_operations_struct tcmu_vm_ops = { | ||
720 | .fault = tcmu_vma_fault, | ||
721 | }; | ||
722 | |||
723 | static int tcmu_mmap(struct uio_info *info, struct vm_area_struct *vma) | ||
724 | { | ||
725 | struct tcmu_dev *udev = container_of(info, struct tcmu_dev, uio_info); | ||
726 | |||
727 | vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; | ||
728 | vma->vm_ops = &tcmu_vm_ops; | ||
729 | |||
730 | vma->vm_private_data = udev; | ||
731 | |||
732 | /* Ensure the mmap is exactly the right size */ | ||
733 | if (vma_pages(vma) != (TCMU_RING_SIZE >> PAGE_SHIFT)) | ||
734 | return -EINVAL; | ||
735 | |||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | static int tcmu_open(struct uio_info *info, struct inode *inode) | ||
740 | { | ||
741 | struct tcmu_dev *udev = container_of(info, struct tcmu_dev, uio_info); | ||
742 | |||
743 | /* O_EXCL not supported for char devs, so fake it? */ | ||
744 | if (test_and_set_bit(TCMU_DEV_BIT_OPEN, &udev->flags)) | ||
745 | return -EBUSY; | ||
746 | |||
747 | pr_debug("open\n"); | ||
748 | |||
749 | return 0; | ||
750 | } | ||
751 | |||
752 | static int tcmu_release(struct uio_info *info, struct inode *inode) | ||
753 | { | ||
754 | struct tcmu_dev *udev = container_of(info, struct tcmu_dev, uio_info); | ||
755 | |||
756 | clear_bit(TCMU_DEV_BIT_OPEN, &udev->flags); | ||
757 | |||
758 | pr_debug("close\n"); | ||
759 | |||
760 | return 0; | ||
761 | } | ||
762 | |||
763 | static int tcmu_netlink_event(enum tcmu_genl_cmd cmd, const char *name, int minor) | ||
764 | { | ||
765 | struct sk_buff *skb; | ||
766 | void *msg_header; | ||
767 | int ret = -ENOMEM; | ||
768 | |||
769 | skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
770 | if (!skb) | ||
771 | return ret; | ||
772 | |||
773 | msg_header = genlmsg_put(skb, 0, 0, &tcmu_genl_family, 0, cmd); | ||
774 | if (!msg_header) | ||
775 | goto free_skb; | ||
776 | |||
777 | ret = nla_put_string(skb, TCMU_ATTR_DEVICE, name); | ||
778 | if (ret < 0) | ||
779 | goto free_skb; | ||
780 | |||
781 | ret = nla_put_u32(skb, TCMU_ATTR_MINOR, minor); | ||
782 | if (ret < 0) | ||
783 | goto free_skb; | ||
784 | |||
785 | ret = genlmsg_end(skb, msg_header); | ||
786 | if (ret < 0) | ||
787 | goto free_skb; | ||
788 | |||
789 | ret = genlmsg_multicast(&tcmu_genl_family, skb, 0, | ||
790 | TCMU_MCGRP_CONFIG, GFP_KERNEL); | ||
791 | |||
792 | /* We don't care if no one is listening */ | ||
793 | if (ret == -ESRCH) | ||
794 | ret = 0; | ||
795 | |||
796 | return ret; | ||
797 | free_skb: | ||
798 | nlmsg_free(skb); | ||
799 | return ret; | ||
800 | } | ||
801 | |||
802 | static int tcmu_configure_device(struct se_device *dev) | ||
803 | { | ||
804 | struct tcmu_dev *udev = TCMU_DEV(dev); | ||
805 | struct tcmu_hba *hba = udev->hba->hba_ptr; | ||
806 | struct uio_info *info; | ||
807 | struct tcmu_mailbox *mb; | ||
808 | size_t size; | ||
809 | size_t used; | ||
810 | int ret = 0; | ||
811 | char *str; | ||
812 | |||
813 | info = &udev->uio_info; | ||
814 | |||
815 | size = snprintf(NULL, 0, "tcm-user/%u/%s/%s", hba->host_id, udev->name, | ||
816 | udev->dev_config); | ||
817 | size += 1; /* for \0 */ | ||
818 | str = kmalloc(size, GFP_KERNEL); | ||
819 | if (!str) | ||
820 | return -ENOMEM; | ||
821 | |||
822 | used = snprintf(str, size, "tcm-user/%u/%s", hba->host_id, udev->name); | ||
823 | |||
824 | if (udev->dev_config[0]) | ||
825 | snprintf(str + used, size - used, "/%s", udev->dev_config); | ||
826 | |||
827 | info->name = str; | ||
828 | |||
829 | udev->mb_addr = vzalloc(TCMU_RING_SIZE); | ||
830 | if (!udev->mb_addr) { | ||
831 | ret = -ENOMEM; | ||
832 | goto err_vzalloc; | ||
833 | } | ||
834 | |||
835 | /* mailbox fits in first part of CMDR space */ | ||
836 | udev->cmdr_size = CMDR_SIZE - CMDR_OFF; | ||
837 | udev->data_off = CMDR_SIZE; | ||
838 | udev->data_size = TCMU_RING_SIZE - CMDR_SIZE; | ||
839 | |||
840 | mb = udev->mb_addr; | ||
841 | mb->version = 1; | ||
842 | mb->cmdr_off = CMDR_OFF; | ||
843 | mb->cmdr_size = udev->cmdr_size; | ||
844 | |||
845 | WARN_ON(!PAGE_ALIGNED(udev->data_off)); | ||
846 | WARN_ON(udev->data_size % PAGE_SIZE); | ||
847 | |||
848 | info->version = "1"; | ||
849 | |||
850 | info->mem[0].name = "tcm-user command & data buffer"; | ||
851 | info->mem[0].addr = (phys_addr_t) udev->mb_addr; | ||
852 | info->mem[0].size = TCMU_RING_SIZE; | ||
853 | info->mem[0].memtype = UIO_MEM_VIRTUAL; | ||
854 | |||
855 | info->irqcontrol = tcmu_irqcontrol; | ||
856 | info->irq = UIO_IRQ_CUSTOM; | ||
857 | |||
858 | info->mmap = tcmu_mmap; | ||
859 | info->open = tcmu_open; | ||
860 | info->release = tcmu_release; | ||
861 | |||
862 | ret = uio_register_device(tcmu_root_device, info); | ||
863 | if (ret) | ||
864 | goto err_register; | ||
865 | |||
866 | /* Other attributes can be configured in userspace */ | ||
867 | dev->dev_attrib.hw_block_size = 512; | ||
868 | dev->dev_attrib.hw_max_sectors = 128; | ||
869 | dev->dev_attrib.hw_queue_depth = 128; | ||
870 | |||
871 | ret = tcmu_netlink_event(TCMU_CMD_ADDED_DEVICE, udev->uio_info.name, | ||
872 | udev->uio_info.uio_dev->minor); | ||
873 | if (ret) | ||
874 | goto err_netlink; | ||
875 | |||
876 | return 0; | ||
877 | |||
878 | err_netlink: | ||
879 | uio_unregister_device(&udev->uio_info); | ||
880 | err_register: | ||
881 | vfree(udev->mb_addr); | ||
882 | err_vzalloc: | ||
883 | kfree(info->name); | ||
884 | |||
885 | return ret; | ||
886 | } | ||
887 | |||
888 | static int tcmu_check_pending_cmd(int id, void *p, void *data) | ||
889 | { | ||
890 | struct tcmu_cmd *cmd = p; | ||
891 | |||
892 | if (test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) | ||
893 | return 0; | ||
894 | return -EINVAL; | ||
895 | } | ||
896 | |||
897 | static void tcmu_free_device(struct se_device *dev) | ||
898 | { | ||
899 | struct tcmu_dev *udev = TCMU_DEV(dev); | ||
900 | int i; | ||
901 | |||
902 | del_timer_sync(&udev->timeout); | ||
903 | |||
904 | vfree(udev->mb_addr); | ||
905 | |||
906 | /* Upper layer should drain all requests before calling this */ | ||
907 | spin_lock_irq(&udev->commands_lock); | ||
908 | i = idr_for_each(&udev->commands, tcmu_check_pending_cmd, NULL); | ||
909 | idr_destroy(&udev->commands); | ||
910 | spin_unlock_irq(&udev->commands_lock); | ||
911 | WARN_ON(i); | ||
912 | |||
913 | /* Device was configured */ | ||
914 | if (udev->uio_info.uio_dev) { | ||
915 | tcmu_netlink_event(TCMU_CMD_REMOVED_DEVICE, udev->uio_info.name, | ||
916 | udev->uio_info.uio_dev->minor); | ||
917 | |||
918 | uio_unregister_device(&udev->uio_info); | ||
919 | kfree(udev->uio_info.name); | ||
920 | kfree(udev->name); | ||
921 | } | ||
922 | |||
923 | kfree(udev); | ||
924 | } | ||
925 | |||
926 | enum { | ||
927 | Opt_dev_config, Opt_dev_size, Opt_err, Opt_pass_level, | ||
928 | }; | ||
929 | |||
930 | static match_table_t tokens = { | ||
931 | {Opt_dev_config, "dev_config=%s"}, | ||
932 | {Opt_dev_size, "dev_size=%u"}, | ||
933 | {Opt_pass_level, "pass_level=%u"}, | ||
934 | {Opt_err, NULL} | ||
935 | }; | ||
936 | |||
937 | static ssize_t tcmu_set_configfs_dev_params(struct se_device *dev, | ||
938 | const char *page, ssize_t count) | ||
939 | { | ||
940 | struct tcmu_dev *udev = TCMU_DEV(dev); | ||
941 | char *orig, *ptr, *opts, *arg_p; | ||
942 | substring_t args[MAX_OPT_ARGS]; | ||
943 | int ret = 0, token; | ||
944 | int arg; | ||
945 | |||
946 | opts = kstrdup(page, GFP_KERNEL); | ||
947 | if (!opts) | ||
948 | return -ENOMEM; | ||
949 | |||
950 | orig = opts; | ||
951 | |||
952 | while ((ptr = strsep(&opts, ",\n")) != NULL) { | ||
953 | if (!*ptr) | ||
954 | continue; | ||
955 | |||
956 | token = match_token(ptr, tokens, args); | ||
957 | switch (token) { | ||
958 | case Opt_dev_config: | ||
959 | if (match_strlcpy(udev->dev_config, &args[0], | ||
960 | TCMU_CONFIG_LEN) == 0) { | ||
961 | ret = -EINVAL; | ||
962 | break; | ||
963 | } | ||
964 | pr_debug("TCMU: Referencing Path: %s\n", udev->dev_config); | ||
965 | break; | ||
966 | case Opt_dev_size: | ||
967 | arg_p = match_strdup(&args[0]); | ||
968 | if (!arg_p) { | ||
969 | ret = -ENOMEM; | ||
970 | break; | ||
971 | } | ||
972 | ret = kstrtoul(arg_p, 0, (unsigned long *) &udev->dev_size); | ||
973 | kfree(arg_p); | ||
974 | if (ret < 0) | ||
975 | pr_err("kstrtoul() failed for dev_size=\n"); | ||
976 | break; | ||
977 | case Opt_pass_level: | ||
978 | match_int(args, &arg); | ||
979 | if (arg >= TCMU_PASS_INVALID) { | ||
980 | pr_warn("TCMU: Invalid pass_level: %d\n", arg); | ||
981 | break; | ||
982 | } | ||
983 | |||
984 | pr_debug("TCMU: Setting pass_level to %d\n", arg); | ||
985 | udev->pass_level = arg; | ||
986 | break; | ||
987 | default: | ||
988 | break; | ||
989 | } | ||
990 | } | ||
991 | |||
992 | kfree(orig); | ||
993 | return (!ret) ? count : ret; | ||
994 | } | ||
995 | |||
996 | static ssize_t tcmu_show_configfs_dev_params(struct se_device *dev, char *b) | ||
997 | { | ||
998 | struct tcmu_dev *udev = TCMU_DEV(dev); | ||
999 | ssize_t bl = 0; | ||
1000 | |||
1001 | bl = sprintf(b + bl, "Config: %s ", | ||
1002 | udev->dev_config[0] ? udev->dev_config : "NULL"); | ||
1003 | bl += sprintf(b + bl, "Size: %zu PassLevel: %u\n", | ||
1004 | udev->dev_size, udev->pass_level); | ||
1005 | |||
1006 | return bl; | ||
1007 | } | ||
1008 | |||
1009 | static sector_t tcmu_get_blocks(struct se_device *dev) | ||
1010 | { | ||
1011 | struct tcmu_dev *udev = TCMU_DEV(dev); | ||
1012 | |||
1013 | return div_u64(udev->dev_size - dev->dev_attrib.block_size, | ||
1014 | dev->dev_attrib.block_size); | ||
1015 | } | ||
1016 | |||
1017 | static sense_reason_t | ||
1018 | tcmu_execute_rw(struct se_cmd *se_cmd, struct scatterlist *sgl, u32 sgl_nents, | ||
1019 | enum dma_data_direction data_direction) | ||
1020 | { | ||
1021 | int ret; | ||
1022 | |||
1023 | ret = tcmu_queue_cmd(se_cmd); | ||
1024 | |||
1025 | if (ret != 0) | ||
1026 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | ||
1027 | else | ||
1028 | return TCM_NO_SENSE; | ||
1029 | } | ||
1030 | |||
1031 | static sense_reason_t | ||
1032 | tcmu_pass_op(struct se_cmd *se_cmd) | ||
1033 | { | ||
1034 | int ret = tcmu_queue_cmd(se_cmd); | ||
1035 | |||
1036 | if (ret != 0) | ||
1037 | return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | ||
1038 | else | ||
1039 | return TCM_NO_SENSE; | ||
1040 | } | ||
1041 | |||
1042 | static struct sbc_ops tcmu_sbc_ops = { | ||
1043 | .execute_rw = tcmu_execute_rw, | ||
1044 | .execute_sync_cache = tcmu_pass_op, | ||
1045 | .execute_write_same = tcmu_pass_op, | ||
1046 | .execute_write_same_unmap = tcmu_pass_op, | ||
1047 | .execute_unmap = tcmu_pass_op, | ||
1048 | }; | ||
1049 | |||
1050 | static sense_reason_t | ||
1051 | tcmu_parse_cdb(struct se_cmd *cmd) | ||
1052 | { | ||
1053 | unsigned char *cdb = cmd->t_task_cdb; | ||
1054 | struct tcmu_dev *udev = TCMU_DEV(cmd->se_dev); | ||
1055 | sense_reason_t ret; | ||
1056 | |||
1057 | switch (udev->pass_level) { | ||
1058 | case TCMU_PASS_ALL: | ||
1059 | /* We're just like pscsi, then */ | ||
1060 | /* | ||
1061 | * For REPORT LUNS we always need to emulate the response, for everything | ||
1062 | * else, pass it up. | ||
1063 | */ | ||
1064 | switch (cdb[0]) { | ||
1065 | case REPORT_LUNS: | ||
1066 | cmd->execute_cmd = spc_emulate_report_luns; | ||
1067 | break; | ||
1068 | case READ_6: | ||
1069 | case READ_10: | ||
1070 | case READ_12: | ||
1071 | case READ_16: | ||
1072 | case WRITE_6: | ||
1073 | case WRITE_10: | ||
1074 | case WRITE_12: | ||
1075 | case WRITE_16: | ||
1076 | case WRITE_VERIFY: | ||
1077 | cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; | ||
1078 | /* FALLTHROUGH */ | ||
1079 | default: | ||
1080 | cmd->execute_cmd = tcmu_pass_op; | ||
1081 | } | ||
1082 | ret = TCM_NO_SENSE; | ||
1083 | break; | ||
1084 | case TCMU_PASS_IO: | ||
1085 | ret = sbc_parse_cdb(cmd, &tcmu_sbc_ops); | ||
1086 | break; | ||
1087 | default: | ||
1088 | pr_err("Unknown tcm-user pass level %d\n", udev->pass_level); | ||
1089 | ret = TCM_CHECK_CONDITION_ABORT_CMD; | ||
1090 | } | ||
1091 | |||
1092 | return ret; | ||
1093 | } | ||
1094 | |||
1095 | static struct se_subsystem_api tcmu_template = { | ||
1096 | .name = "user", | ||
1097 | .inquiry_prod = "USER", | ||
1098 | .inquiry_rev = TCMU_VERSION, | ||
1099 | .owner = THIS_MODULE, | ||
1100 | .transport_type = TRANSPORT_PLUGIN_VHBA_PDEV, | ||
1101 | .attach_hba = tcmu_attach_hba, | ||
1102 | .detach_hba = tcmu_detach_hba, | ||
1103 | .alloc_device = tcmu_alloc_device, | ||
1104 | .configure_device = tcmu_configure_device, | ||
1105 | .free_device = tcmu_free_device, | ||
1106 | .parse_cdb = tcmu_parse_cdb, | ||
1107 | .set_configfs_dev_params = tcmu_set_configfs_dev_params, | ||
1108 | .show_configfs_dev_params = tcmu_show_configfs_dev_params, | ||
1109 | .get_device_type = sbc_get_device_type, | ||
1110 | .get_blocks = tcmu_get_blocks, | ||
1111 | }; | ||
1112 | |||
1113 | static int __init tcmu_module_init(void) | ||
1114 | { | ||
1115 | int ret; | ||
1116 | |||
1117 | BUILD_BUG_ON((sizeof(struct tcmu_cmd_entry) % TCMU_OP_ALIGN_SIZE) != 0); | ||
1118 | |||
1119 | tcmu_cmd_cache = kmem_cache_create("tcmu_cmd_cache", | ||
1120 | sizeof(struct tcmu_cmd), | ||
1121 | __alignof__(struct tcmu_cmd), | ||
1122 | 0, NULL); | ||
1123 | if (!tcmu_cmd_cache) | ||
1124 | return -ENOMEM; | ||
1125 | |||
1126 | tcmu_root_device = root_device_register("tcm_user"); | ||
1127 | if (IS_ERR(tcmu_root_device)) { | ||
1128 | ret = PTR_ERR(tcmu_root_device); | ||
1129 | goto out_free_cache; | ||
1130 | } | ||
1131 | |||
1132 | ret = genl_register_family(&tcmu_genl_family); | ||
1133 | if (ret < 0) { | ||
1134 | goto out_unreg_device; | ||
1135 | } | ||
1136 | |||
1137 | ret = transport_subsystem_register(&tcmu_template); | ||
1138 | if (ret) | ||
1139 | goto out_unreg_genl; | ||
1140 | |||
1141 | return 0; | ||
1142 | |||
1143 | out_unreg_genl: | ||
1144 | genl_unregister_family(&tcmu_genl_family); | ||
1145 | out_unreg_device: | ||
1146 | root_device_unregister(tcmu_root_device); | ||
1147 | out_free_cache: | ||
1148 | kmem_cache_destroy(tcmu_cmd_cache); | ||
1149 | |||
1150 | return ret; | ||
1151 | } | ||
1152 | |||
1153 | static void __exit tcmu_module_exit(void) | ||
1154 | { | ||
1155 | transport_subsystem_release(&tcmu_template); | ||
1156 | genl_unregister_family(&tcmu_genl_family); | ||
1157 | root_device_unregister(tcmu_root_device); | ||
1158 | kmem_cache_destroy(tcmu_cmd_cache); | ||
1159 | } | ||
1160 | |||
1161 | MODULE_DESCRIPTION("TCM USER subsystem plugin"); | ||
1162 | MODULE_AUTHOR("Shaohua Li <shli@kernel.org>"); | ||
1163 | MODULE_AUTHOR("Andy Grover <agrover@redhat.com>"); | ||
1164 | MODULE_LICENSE("GPL"); | ||
1165 | |||
1166 | module_init(tcmu_module_init); | ||
1167 | module_exit(tcmu_module_exit); | ||
diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index 21ce50880c79..ccee7e332a4d 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c | |||
@@ -98,7 +98,7 @@ static void ft_tport_delete(struct ft_tport *tport) | |||
98 | ft_sess_delete_all(tport); | 98 | ft_sess_delete_all(tport); |
99 | lport = tport->lport; | 99 | lport = tport->lport; |
100 | BUG_ON(tport != lport->prov[FC_TYPE_FCP]); | 100 | BUG_ON(tport != lport->prov[FC_TYPE_FCP]); |
101 | rcu_assign_pointer(lport->prov[FC_TYPE_FCP], NULL); | 101 | RCU_INIT_POINTER(lport->prov[FC_TYPE_FCP], NULL); |
102 | 102 | ||
103 | tpg = tport->tpg; | 103 | tpg = tport->tpg; |
104 | if (tpg) { | 104 | if (tpg) { |
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index ef5587fe2c69..f554d25b4399 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig | |||
@@ -84,6 +84,16 @@ config THERMAL_GOV_STEP_WISE | |||
84 | Enable this to manage platform thermals using a simple linear | 84 | Enable this to manage platform thermals using a simple linear |
85 | governor. | 85 | governor. |
86 | 86 | ||
87 | config THERMAL_GOV_BANG_BANG | ||
88 | bool "Bang Bang thermal governor" | ||
89 | default n | ||
90 | help | ||
91 | Enable this to manage platform thermals using bang bang governor. | ||
92 | |||
93 | Say 'Y' here if you want to use two point temperature regulation | ||
94 | used for fans without throttling. Some fan drivers depend on this | ||
95 | governor to be enabled (e.g. acerhdf). | ||
96 | |||
87 | config THERMAL_GOV_USER_SPACE | 97 | config THERMAL_GOV_USER_SPACE |
88 | bool "User_space thermal governor" | 98 | bool "User_space thermal governor" |
89 | help | 99 | help |
@@ -207,21 +217,6 @@ config X86_PKG_TEMP_THERMAL | |||
207 | two trip points which can be set by user to get notifications via thermal | 217 | two trip points which can be set by user to get notifications via thermal |
208 | notification methods. | 218 | notification methods. |
209 | 219 | ||
210 | config ACPI_INT3403_THERMAL | ||
211 | tristate "ACPI INT3403 thermal driver" | ||
212 | depends on X86 && ACPI | ||
213 | help | ||
214 | Newer laptops and tablets that use ACPI may have thermal sensors | ||
215 | outside the core CPU/SOC for thermal safety reasons. These | ||
216 | temperature sensors are also exposed for the OS to use via the so | ||
217 | called INT3403 ACPI object. This driver will, on devices that have | ||
218 | such sensors, expose the temperature information from these sensors | ||
219 | to userspace via the normal thermal framework. This means that a wide | ||
220 | range of applications and GUI widgets can show this information to | ||
221 | the user or use this information for making decisions. For example, | ||
222 | the Intel Thermal Daemon can use this information to allow the user | ||
223 | to select his laptop to run without turning on the fans. | ||
224 | |||
225 | config INTEL_SOC_DTS_THERMAL | 220 | config INTEL_SOC_DTS_THERMAL |
226 | tristate "Intel SoCs DTS thermal driver" | 221 | tristate "Intel SoCs DTS thermal driver" |
227 | depends on X86 && IOSF_MBI | 222 | depends on X86 && IOSF_MBI |
@@ -234,6 +229,30 @@ config INTEL_SOC_DTS_THERMAL | |||
234 | notification methods.The other trip is a critical trip point, which | 229 | notification methods.The other trip is a critical trip point, which |
235 | was set by the driver based on the TJ MAX temperature. | 230 | was set by the driver based on the TJ MAX temperature. |
236 | 231 | ||
232 | config INT340X_THERMAL | ||
233 | tristate "ACPI INT340X thermal drivers" | ||
234 | depends on X86 && ACPI | ||
235 | select THERMAL_GOV_USER_SPACE | ||
236 | select ACPI_THERMAL_REL | ||
237 | select ACPI_FAN | ||
238 | help | ||
239 | Newer laptops and tablets that use ACPI may have thermal sensors and | ||
240 | other devices with thermal control capabilities outside the core | ||
241 | CPU/SOC, for thermal safety reasons. | ||
242 | They are exposed for the OS to use via the INT3400 ACPI device object | ||
243 | as the master, and INT3401~INT340B ACPI device objects as the slaves. | ||
244 | Enable this to expose the temperature information and cooling ability | ||
245 | from these objects to userspace via the normal thermal framework. | ||
246 | This means that a wide range of applications and GUI widgets can show | ||
247 | the information to the user or use this information for making | ||
248 | decisions. For example, the Intel Thermal Daemon can use this | ||
249 | information to allow the user to select his laptop to run without | ||
250 | turning on the fans. | ||
251 | |||
252 | config ACPI_THERMAL_REL | ||
253 | tristate | ||
254 | depends on ACPI | ||
255 | |||
237 | menu "Texas Instruments thermal drivers" | 256 | menu "Texas Instruments thermal drivers" |
238 | source "drivers/thermal/ti-soc-thermal/Kconfig" | 257 | source "drivers/thermal/ti-soc-thermal/Kconfig" |
239 | endmenu | 258 | endmenu |
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 31e232f84b6b..39c4fe87da2f 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile | |||
@@ -11,6 +11,7 @@ thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o | |||
11 | 11 | ||
12 | # governors | 12 | # governors |
13 | thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o | 13 | thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o |
14 | thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG) += gov_bang_bang.o | ||
14 | thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o | 15 | thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o |
15 | thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o | 16 | thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o |
16 | 17 | ||
@@ -31,5 +32,5 @@ obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o | |||
31 | obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o | 32 | obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o |
32 | obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o | 33 | obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o |
33 | obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ | 34 | obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ |
34 | obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o | 35 | obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ |
35 | obj-$(CONFIG_ST_THERMAL) += st/ | 36 | obj-$(CONFIG_ST_THERMAL) += st/ |
diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c index 944ba2f340c8..6e0a3fbfae86 100644 --- a/drivers/thermal/fair_share.c +++ b/drivers/thermal/fair_share.c | |||
@@ -23,6 +23,7 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/thermal.h> | 25 | #include <linux/thermal.h> |
26 | #include <trace/events/thermal.h> | ||
26 | 27 | ||
27 | #include "thermal_core.h" | 28 | #include "thermal_core.h" |
28 | 29 | ||
@@ -34,6 +35,7 @@ static int get_trip_level(struct thermal_zone_device *tz) | |||
34 | { | 35 | { |
35 | int count = 0; | 36 | int count = 0; |
36 | unsigned long trip_temp; | 37 | unsigned long trip_temp; |
38 | enum thermal_trip_type trip_type; | ||
37 | 39 | ||
38 | if (tz->trips == 0 || !tz->ops->get_trip_temp) | 40 | if (tz->trips == 0 || !tz->ops->get_trip_temp) |
39 | return 0; | 41 | return 0; |
@@ -43,6 +45,16 @@ static int get_trip_level(struct thermal_zone_device *tz) | |||
43 | if (tz->temperature < trip_temp) | 45 | if (tz->temperature < trip_temp) |
44 | break; | 46 | break; |
45 | } | 47 | } |
48 | |||
49 | /* | ||
50 | * count > 0 only if temperature is greater than first trip | ||
51 | * point, in which case, trip_point = count - 1 | ||
52 | */ | ||
53 | if (count > 0) { | ||
54 | tz->ops->get_trip_type(tz, count - 1, &trip_type); | ||
55 | trace_thermal_zone_trip(tz, count - 1, trip_type); | ||
56 | } | ||
57 | |||
46 | return count; | 58 | return count; |
47 | } | 59 | } |
48 | 60 | ||
diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c new file mode 100644 index 000000000000..c5dd76b2ee74 --- /dev/null +++ b/drivers/thermal/gov_bang_bang.c | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * gov_bang_bang.c - A simple thermal throttling governor using hysteresis | ||
3 | * | ||
4 | * Copyright (C) 2014 Peter Feuerer <peter@piie.net> | ||
5 | * | ||
6 | * Based on step_wise.c with following Copyrights: | ||
7 | * Copyright (C) 2012 Intel Corp | ||
8 | * Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com> | ||
9 | * | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation, version 2. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | ||
18 | * the GNU General Public License for more details. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/thermal.h> | ||
23 | |||
24 | #include "thermal_core.h" | ||
25 | |||
26 | static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) | ||
27 | { | ||
28 | long trip_temp; | ||
29 | unsigned long trip_hyst; | ||
30 | struct thermal_instance *instance; | ||
31 | |||
32 | tz->ops->get_trip_temp(tz, trip, &trip_temp); | ||
33 | tz->ops->get_trip_hyst(tz, trip, &trip_hyst); | ||
34 | |||
35 | dev_dbg(&tz->device, "Trip%d[temp=%ld]:temp=%d:hyst=%ld\n", | ||
36 | trip, trip_temp, tz->temperature, | ||
37 | trip_hyst); | ||
38 | |||
39 | mutex_lock(&tz->lock); | ||
40 | |||
41 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) { | ||
42 | if (instance->trip != trip) | ||
43 | continue; | ||
44 | |||
45 | /* in case fan is in initial state, switch the fan off */ | ||
46 | if (instance->target == THERMAL_NO_TARGET) | ||
47 | instance->target = 0; | ||
48 | |||
49 | /* in case fan is neither on nor off set the fan to active */ | ||
50 | if (instance->target != 0 && instance->target != 1) { | ||
51 | pr_warn("Thermal instance %s controlled by bang-bang has unexpected state: %ld\n", | ||
52 | instance->name, instance->target); | ||
53 | instance->target = 1; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * enable fan when temperature exceeds trip_temp and disable | ||
58 | * the fan in case it falls below trip_temp minus hysteresis | ||
59 | */ | ||
60 | if (instance->target == 0 && tz->temperature >= trip_temp) | ||
61 | instance->target = 1; | ||
62 | else if (instance->target == 1 && | ||
63 | tz->temperature < trip_temp - trip_hyst) | ||
64 | instance->target = 0; | ||
65 | |||
66 | dev_dbg(&instance->cdev->device, "target=%d\n", | ||
67 | (int)instance->target); | ||
68 | |||
69 | instance->cdev->updated = false; /* cdev needs update */ | ||
70 | } | ||
71 | |||
72 | mutex_unlock(&tz->lock); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * bang_bang_control - controls devices associated with the given zone | ||
77 | * @tz - thermal_zone_device | ||
78 | * @trip - the trip point | ||
79 | * | ||
80 | * Regulation Logic: a two point regulation, deliver cooling state depending | ||
81 | * on the previous state shown in this diagram: | ||
82 | * | ||
83 | * Fan: OFF ON | ||
84 | * | ||
85 | * | | ||
86 | * | | ||
87 | * trip_temp: +---->+ | ||
88 | * | | ^ | ||
89 | * | | | | ||
90 | * | | Temperature | ||
91 | * (trip_temp - hyst): +<----+ | ||
92 | * | | ||
93 | * | | ||
94 | * | | ||
95 | * | ||
96 | * * If the fan is not running and temperature exceeds trip_temp, the fan | ||
97 | * gets turned on. | ||
98 | * * In case the fan is running, temperature must fall below | ||
99 | * (trip_temp - hyst) so that the fan gets turned off again. | ||
100 | * | ||
101 | */ | ||
102 | static int bang_bang_control(struct thermal_zone_device *tz, int trip) | ||
103 | { | ||
104 | struct thermal_instance *instance; | ||
105 | |||
106 | thermal_zone_trip_update(tz, trip); | ||
107 | |||
108 | mutex_lock(&tz->lock); | ||
109 | |||
110 | list_for_each_entry(instance, &tz->thermal_instances, tz_node) | ||
111 | thermal_cdev_update(instance->cdev); | ||
112 | |||
113 | mutex_unlock(&tz->lock); | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static struct thermal_governor thermal_gov_bang_bang = { | ||
119 | .name = "bang_bang", | ||
120 | .throttle = bang_bang_control, | ||
121 | }; | ||
122 | |||
123 | int thermal_gov_bang_bang_register(void) | ||
124 | { | ||
125 | return thermal_register_governor(&thermal_gov_bang_bang); | ||
126 | } | ||
127 | |||
128 | void thermal_gov_bang_bang_unregister(void) | ||
129 | { | ||
130 | thermal_unregister_governor(&thermal_gov_bang_bang); | ||
131 | } | ||
diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 2c516f2eebed..461bf3d033a0 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/mfd/syscon.h> | 19 | #include <linux/mfd/syscon.h> |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/of.h> | 21 | #include <linux/of.h> |
22 | #include <linux/of_device.h> | ||
22 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
23 | #include <linux/regmap.h> | 24 | #include <linux/regmap.h> |
24 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
@@ -31,6 +32,11 @@ | |||
31 | 32 | ||
32 | #define MISC0 0x0150 | 33 | #define MISC0 0x0150 |
33 | #define MISC0_REFTOP_SELBIASOFF (1 << 3) | 34 | #define MISC0_REFTOP_SELBIASOFF (1 << 3) |
35 | #define MISC1 0x0160 | ||
36 | #define MISC1_IRQ_TEMPHIGH (1 << 29) | ||
37 | /* Below LOW and PANIC bits are only for TEMPMON_IMX6SX */ | ||
38 | #define MISC1_IRQ_TEMPLOW (1 << 28) | ||
39 | #define MISC1_IRQ_TEMPPANIC (1 << 27) | ||
34 | 40 | ||
35 | #define TEMPSENSE0 0x0180 | 41 | #define TEMPSENSE0 0x0180 |
36 | #define TEMPSENSE0_ALARM_VALUE_SHIFT 20 | 42 | #define TEMPSENSE0_ALARM_VALUE_SHIFT 20 |
@@ -43,6 +49,12 @@ | |||
43 | 49 | ||
44 | #define TEMPSENSE1 0x0190 | 50 | #define TEMPSENSE1 0x0190 |
45 | #define TEMPSENSE1_MEASURE_FREQ 0xffff | 51 | #define TEMPSENSE1_MEASURE_FREQ 0xffff |
52 | /* Below TEMPSENSE2 is only for TEMPMON_IMX6SX */ | ||
53 | #define TEMPSENSE2 0x0290 | ||
54 | #define TEMPSENSE2_LOW_VALUE_SHIFT 0 | ||
55 | #define TEMPSENSE2_LOW_VALUE_MASK 0xfff | ||
56 | #define TEMPSENSE2_PANIC_VALUE_SHIFT 16 | ||
57 | #define TEMPSENSE2_PANIC_VALUE_MASK 0xfff0000 | ||
46 | 58 | ||
47 | #define OCOTP_ANA1 0x04e0 | 59 | #define OCOTP_ANA1 0x04e0 |
48 | 60 | ||
@@ -66,6 +78,21 @@ enum imx_thermal_trip { | |||
66 | #define FACTOR1 15976 | 78 | #define FACTOR1 15976 |
67 | #define FACTOR2 4297157 | 79 | #define FACTOR2 4297157 |
68 | 80 | ||
81 | #define TEMPMON_IMX6Q 1 | ||
82 | #define TEMPMON_IMX6SX 2 | ||
83 | |||
84 | struct thermal_soc_data { | ||
85 | u32 version; | ||
86 | }; | ||
87 | |||
88 | static struct thermal_soc_data thermal_imx6q_data = { | ||
89 | .version = TEMPMON_IMX6Q, | ||
90 | }; | ||
91 | |||
92 | static struct thermal_soc_data thermal_imx6sx_data = { | ||
93 | .version = TEMPMON_IMX6SX, | ||
94 | }; | ||
95 | |||
69 | struct imx_thermal_data { | 96 | struct imx_thermal_data { |
70 | struct thermal_zone_device *tz; | 97 | struct thermal_zone_device *tz; |
71 | struct thermal_cooling_device *cdev; | 98 | struct thermal_cooling_device *cdev; |
@@ -79,8 +106,21 @@ struct imx_thermal_data { | |||
79 | bool irq_enabled; | 106 | bool irq_enabled; |
80 | int irq; | 107 | int irq; |
81 | struct clk *thermal_clk; | 108 | struct clk *thermal_clk; |
109 | const struct thermal_soc_data *socdata; | ||
82 | }; | 110 | }; |
83 | 111 | ||
112 | static void imx_set_panic_temp(struct imx_thermal_data *data, | ||
113 | signed long panic_temp) | ||
114 | { | ||
115 | struct regmap *map = data->tempmon; | ||
116 | int critical_value; | ||
117 | |||
118 | critical_value = (data->c2 - panic_temp) / data->c1; | ||
119 | regmap_write(map, TEMPSENSE2 + REG_CLR, TEMPSENSE2_PANIC_VALUE_MASK); | ||
120 | regmap_write(map, TEMPSENSE2 + REG_SET, critical_value << | ||
121 | TEMPSENSE2_PANIC_VALUE_SHIFT); | ||
122 | } | ||
123 | |||
84 | static void imx_set_alarm_temp(struct imx_thermal_data *data, | 124 | static void imx_set_alarm_temp(struct imx_thermal_data *data, |
85 | signed long alarm_temp) | 125 | signed long alarm_temp) |
86 | { | 126 | { |
@@ -142,13 +182,17 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) | |||
142 | /* See imx_get_sensor_data() for formula derivation */ | 182 | /* See imx_get_sensor_data() for formula derivation */ |
143 | *temp = data->c2 - n_meas * data->c1; | 183 | *temp = data->c2 - n_meas * data->c1; |
144 | 184 | ||
145 | /* Update alarm value to next higher trip point */ | 185 | /* Update alarm value to next higher trip point for TEMPMON_IMX6Q */ |
146 | if (data->alarm_temp == data->temp_passive && *temp >= data->temp_passive) | 186 | if (data->socdata->version == TEMPMON_IMX6Q) { |
147 | imx_set_alarm_temp(data, data->temp_critical); | 187 | if (data->alarm_temp == data->temp_passive && |
148 | if (data->alarm_temp == data->temp_critical && *temp < data->temp_passive) { | 188 | *temp >= data->temp_passive) |
149 | imx_set_alarm_temp(data, data->temp_passive); | 189 | imx_set_alarm_temp(data, data->temp_critical); |
150 | dev_dbg(&tz->device, "thermal alarm off: T < %lu\n", | 190 | if (data->alarm_temp == data->temp_critical && |
151 | data->alarm_temp / 1000); | 191 | *temp < data->temp_passive) { |
192 | imx_set_alarm_temp(data, data->temp_passive); | ||
193 | dev_dbg(&tz->device, "thermal alarm off: T < %lu\n", | ||
194 | data->alarm_temp / 1000); | ||
195 | } | ||
152 | } | 196 | } |
153 | 197 | ||
154 | if (*temp != data->last_temp) { | 198 | if (*temp != data->last_temp) { |
@@ -398,8 +442,17 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev) | |||
398 | return IRQ_HANDLED; | 442 | return IRQ_HANDLED; |
399 | } | 443 | } |
400 | 444 | ||
445 | static const struct of_device_id of_imx_thermal_match[] = { | ||
446 | { .compatible = "fsl,imx6q-tempmon", .data = &thermal_imx6q_data, }, | ||
447 | { .compatible = "fsl,imx6sx-tempmon", .data = &thermal_imx6sx_data, }, | ||
448 | { /* end */ } | ||
449 | }; | ||
450 | MODULE_DEVICE_TABLE(of, of_imx_thermal_match); | ||
451 | |||
401 | static int imx_thermal_probe(struct platform_device *pdev) | 452 | static int imx_thermal_probe(struct platform_device *pdev) |
402 | { | 453 | { |
454 | const struct of_device_id *of_id = | ||
455 | of_match_device(of_imx_thermal_match, &pdev->dev); | ||
403 | struct imx_thermal_data *data; | 456 | struct imx_thermal_data *data; |
404 | struct cpumask clip_cpus; | 457 | struct cpumask clip_cpus; |
405 | struct regmap *map; | 458 | struct regmap *map; |
@@ -418,6 +471,20 @@ static int imx_thermal_probe(struct platform_device *pdev) | |||
418 | } | 471 | } |
419 | data->tempmon = map; | 472 | data->tempmon = map; |
420 | 473 | ||
474 | data->socdata = of_id->data; | ||
475 | |||
476 | /* make sure the IRQ flag is clear before enabling irq on i.MX6SX */ | ||
477 | if (data->socdata->version == TEMPMON_IMX6SX) { | ||
478 | regmap_write(map, MISC1 + REG_CLR, MISC1_IRQ_TEMPHIGH | | ||
479 | MISC1_IRQ_TEMPLOW | MISC1_IRQ_TEMPPANIC); | ||
480 | /* | ||
481 | * reset value of LOW ALARM is incorrect, set it to lowest | ||
482 | * value to avoid false trigger of low alarm. | ||
483 | */ | ||
484 | regmap_write(map, TEMPSENSE2 + REG_SET, | ||
485 | TEMPSENSE2_LOW_VALUE_MASK); | ||
486 | } | ||
487 | |||
421 | data->irq = platform_get_irq(pdev, 0); | 488 | data->irq = platform_get_irq(pdev, 0); |
422 | if (data->irq < 0) | 489 | if (data->irq < 0) |
423 | return data->irq; | 490 | return data->irq; |
@@ -489,6 +556,10 @@ static int imx_thermal_probe(struct platform_device *pdev) | |||
489 | measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */ | 556 | measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */ |
490 | regmap_write(map, TEMPSENSE1 + REG_SET, measure_freq); | 557 | regmap_write(map, TEMPSENSE1 + REG_SET, measure_freq); |
491 | imx_set_alarm_temp(data, data->temp_passive); | 558 | imx_set_alarm_temp(data, data->temp_passive); |
559 | |||
560 | if (data->socdata->version == TEMPMON_IMX6SX) | ||
561 | imx_set_panic_temp(data, data->temp_critical); | ||
562 | |||
492 | regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); | 563 | regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); |
493 | regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); | 564 | regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); |
494 | 565 | ||
@@ -550,12 +621,6 @@ static int imx_thermal_resume(struct device *dev) | |||
550 | static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops, | 621 | static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops, |
551 | imx_thermal_suspend, imx_thermal_resume); | 622 | imx_thermal_suspend, imx_thermal_resume); |
552 | 623 | ||
553 | static const struct of_device_id of_imx_thermal_match[] = { | ||
554 | { .compatible = "fsl,imx6q-tempmon", }, | ||
555 | { /* end */ } | ||
556 | }; | ||
557 | MODULE_DEVICE_TABLE(of, of_imx_thermal_match); | ||
558 | |||
559 | static struct platform_driver imx_thermal = { | 624 | static struct platform_driver imx_thermal = { |
560 | .driver = { | 625 | .driver = { |
561 | .name = "imx_thermal", | 626 | .name = "imx_thermal", |
diff --git a/drivers/thermal/int3403_thermal.c b/drivers/thermal/int3403_thermal.c deleted file mode 100644 index 17554eeb3953..000000000000 --- a/drivers/thermal/int3403_thermal.c +++ /dev/null | |||
@@ -1,296 +0,0 @@ | |||
1 | /* | ||
2 | * ACPI INT3403 thermal driver | ||
3 | * Copyright (c) 2013, Intel Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/acpi.h> | ||
20 | #include <linux/thermal.h> | ||
21 | |||
22 | #define INT3403_TYPE_SENSOR 0x03 | ||
23 | #define INT3403_PERF_CHANGED_EVENT 0x80 | ||
24 | #define INT3403_THERMAL_EVENT 0x90 | ||
25 | |||
26 | #define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100) | ||
27 | #define KELVIN_OFFSET 2732 | ||
28 | #define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off)) | ||
29 | |||
30 | #define ACPI_INT3403_CLASS "int3403" | ||
31 | #define ACPI_INT3403_FILE_STATE "state" | ||
32 | |||
33 | struct int3403_sensor { | ||
34 | struct thermal_zone_device *tzone; | ||
35 | unsigned long *thresholds; | ||
36 | unsigned long crit_temp; | ||
37 | int crit_trip_id; | ||
38 | unsigned long psv_temp; | ||
39 | int psv_trip_id; | ||
40 | }; | ||
41 | |||
42 | static int sys_get_curr_temp(struct thermal_zone_device *tzone, | ||
43 | unsigned long *temp) | ||
44 | { | ||
45 | struct acpi_device *device = tzone->devdata; | ||
46 | unsigned long long tmp; | ||
47 | acpi_status status; | ||
48 | |||
49 | status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp); | ||
50 | if (ACPI_FAILURE(status)) | ||
51 | return -EIO; | ||
52 | |||
53 | *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET); | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int sys_get_trip_hyst(struct thermal_zone_device *tzone, | ||
59 | int trip, unsigned long *temp) | ||
60 | { | ||
61 | struct acpi_device *device = tzone->devdata; | ||
62 | unsigned long long hyst; | ||
63 | acpi_status status; | ||
64 | |||
65 | status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst); | ||
66 | if (ACPI_FAILURE(status)) | ||
67 | return -EIO; | ||
68 | |||
69 | /* | ||
70 | * Thermal hysteresis represents a temperature difference. | ||
71 | * Kelvin and Celsius have same degree size. So the | ||
72 | * conversion here between tenths of degree Kelvin unit | ||
73 | * and Milli-Celsius unit is just to multiply 100. | ||
74 | */ | ||
75 | *temp = hyst * 100; | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int sys_get_trip_temp(struct thermal_zone_device *tzone, | ||
81 | int trip, unsigned long *temp) | ||
82 | { | ||
83 | struct acpi_device *device = tzone->devdata; | ||
84 | struct int3403_sensor *obj = acpi_driver_data(device); | ||
85 | |||
86 | if (trip == obj->crit_trip_id) | ||
87 | *temp = obj->crit_temp; | ||
88 | else if (trip == obj->psv_trip_id) | ||
89 | *temp = obj->psv_temp; | ||
90 | else { | ||
91 | /* | ||
92 | * get_trip_temp is a mandatory callback but | ||
93 | * PATx method doesn't return any value, so return | ||
94 | * cached value, which was last set from user space. | ||
95 | */ | ||
96 | *temp = obj->thresholds[trip]; | ||
97 | } | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int sys_get_trip_type(struct thermal_zone_device *thermal, | ||
103 | int trip, enum thermal_trip_type *type) | ||
104 | { | ||
105 | struct acpi_device *device = thermal->devdata; | ||
106 | struct int3403_sensor *obj = acpi_driver_data(device); | ||
107 | |||
108 | /* Mandatory callback, may not mean much here */ | ||
109 | if (trip == obj->crit_trip_id) | ||
110 | *type = THERMAL_TRIP_CRITICAL; | ||
111 | else | ||
112 | *type = THERMAL_TRIP_PASSIVE; | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip, | ||
118 | unsigned long temp) | ||
119 | { | ||
120 | struct acpi_device *device = tzone->devdata; | ||
121 | acpi_status status; | ||
122 | char name[10]; | ||
123 | int ret = 0; | ||
124 | struct int3403_sensor *obj = acpi_driver_data(device); | ||
125 | |||
126 | snprintf(name, sizeof(name), "PAT%d", trip); | ||
127 | if (acpi_has_method(device->handle, name)) { | ||
128 | status = acpi_execute_simple_method(device->handle, name, | ||
129 | MILLI_CELSIUS_TO_DECI_KELVIN(temp, | ||
130 | KELVIN_OFFSET)); | ||
131 | if (ACPI_FAILURE(status)) | ||
132 | ret = -EIO; | ||
133 | else | ||
134 | obj->thresholds[trip] = temp; | ||
135 | } else { | ||
136 | ret = -EIO; | ||
137 | dev_err(&device->dev, "sys_set_trip_temp: method not found\n"); | ||
138 | } | ||
139 | |||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | static struct thermal_zone_device_ops tzone_ops = { | ||
144 | .get_temp = sys_get_curr_temp, | ||
145 | .get_trip_temp = sys_get_trip_temp, | ||
146 | .get_trip_type = sys_get_trip_type, | ||
147 | .set_trip_temp = sys_set_trip_temp, | ||
148 | .get_trip_hyst = sys_get_trip_hyst, | ||
149 | }; | ||
150 | |||
151 | static void acpi_thermal_notify(struct acpi_device *device, u32 event) | ||
152 | { | ||
153 | struct int3403_sensor *obj; | ||
154 | |||
155 | if (!device) | ||
156 | return; | ||
157 | |||
158 | obj = acpi_driver_data(device); | ||
159 | if (!obj) | ||
160 | return; | ||
161 | |||
162 | switch (event) { | ||
163 | case INT3403_PERF_CHANGED_EVENT: | ||
164 | break; | ||
165 | case INT3403_THERMAL_EVENT: | ||
166 | thermal_zone_device_update(obj->tzone); | ||
167 | break; | ||
168 | default: | ||
169 | dev_err(&device->dev, "Unsupported event [0x%x]\n", event); | ||
170 | break; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp) | ||
175 | { | ||
176 | unsigned long long crt; | ||
177 | acpi_status status; | ||
178 | |||
179 | status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt); | ||
180 | if (ACPI_FAILURE(status)) | ||
181 | return -EIO; | ||
182 | |||
183 | *temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET); | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp) | ||
189 | { | ||
190 | unsigned long long psv; | ||
191 | acpi_status status; | ||
192 | |||
193 | status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv); | ||
194 | if (ACPI_FAILURE(status)) | ||
195 | return -EIO; | ||
196 | |||
197 | *temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET); | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int acpi_int3403_add(struct acpi_device *device) | ||
203 | { | ||
204 | int result = 0; | ||
205 | unsigned long long ptyp; | ||
206 | acpi_status status; | ||
207 | struct int3403_sensor *obj; | ||
208 | unsigned long long trip_cnt; | ||
209 | int trip_mask = 0; | ||
210 | |||
211 | if (!device) | ||
212 | return -EINVAL; | ||
213 | |||
214 | status = acpi_evaluate_integer(device->handle, "PTYP", NULL, &ptyp); | ||
215 | if (ACPI_FAILURE(status)) | ||
216 | return -EINVAL; | ||
217 | |||
218 | if (ptyp != INT3403_TYPE_SENSOR) | ||
219 | return -EINVAL; | ||
220 | |||
221 | obj = devm_kzalloc(&device->dev, sizeof(*obj), GFP_KERNEL); | ||
222 | if (!obj) | ||
223 | return -ENOMEM; | ||
224 | |||
225 | device->driver_data = obj; | ||
226 | |||
227 | status = acpi_evaluate_integer(device->handle, "PATC", NULL, | ||
228 | &trip_cnt); | ||
229 | if (ACPI_FAILURE(status)) | ||
230 | trip_cnt = 0; | ||
231 | |||
232 | if (trip_cnt) { | ||
233 | /* We have to cache, thresholds can't be readback */ | ||
234 | obj->thresholds = devm_kzalloc(&device->dev, | ||
235 | sizeof(*obj->thresholds) * trip_cnt, | ||
236 | GFP_KERNEL); | ||
237 | if (!obj->thresholds) | ||
238 | return -ENOMEM; | ||
239 | trip_mask = BIT(trip_cnt) - 1; | ||
240 | } | ||
241 | |||
242 | obj->psv_trip_id = -1; | ||
243 | if (!sys_get_trip_psv(device, &obj->psv_temp)) | ||
244 | obj->psv_trip_id = trip_cnt++; | ||
245 | |||
246 | obj->crit_trip_id = -1; | ||
247 | if (!sys_get_trip_crt(device, &obj->crit_temp)) | ||
248 | obj->crit_trip_id = trip_cnt++; | ||
249 | |||
250 | obj->tzone = thermal_zone_device_register(acpi_device_bid(device), | ||
251 | trip_cnt, trip_mask, device, &tzone_ops, | ||
252 | NULL, 0, 0); | ||
253 | if (IS_ERR(obj->tzone)) { | ||
254 | result = PTR_ERR(obj->tzone); | ||
255 | return result; | ||
256 | } | ||
257 | |||
258 | strcpy(acpi_device_name(device), "INT3403"); | ||
259 | strcpy(acpi_device_class(device), ACPI_INT3403_CLASS); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int acpi_int3403_remove(struct acpi_device *device) | ||
265 | { | ||
266 | struct int3403_sensor *obj; | ||
267 | |||
268 | obj = acpi_driver_data(device); | ||
269 | thermal_zone_device_unregister(obj->tzone); | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | ACPI_MODULE_NAME("int3403"); | ||
275 | static const struct acpi_device_id int3403_device_ids[] = { | ||
276 | {"INT3403", 0}, | ||
277 | {"", 0}, | ||
278 | }; | ||
279 | MODULE_DEVICE_TABLE(acpi, int3403_device_ids); | ||
280 | |||
281 | static struct acpi_driver acpi_int3403_driver = { | ||
282 | .name = "INT3403", | ||
283 | .class = ACPI_INT3403_CLASS, | ||
284 | .ids = int3403_device_ids, | ||
285 | .ops = { | ||
286 | .add = acpi_int3403_add, | ||
287 | .remove = acpi_int3403_remove, | ||
288 | .notify = acpi_thermal_notify, | ||
289 | }, | ||
290 | }; | ||
291 | |||
292 | module_acpi_driver(acpi_int3403_driver); | ||
293 | |||
294 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); | ||
295 | MODULE_LICENSE("GPL v2"); | ||
296 | MODULE_DESCRIPTION("ACPI INT3403 thermal driver"); | ||
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile new file mode 100644 index 000000000000..ffe40bffaf1a --- /dev/null +++ b/drivers/thermal/int340x_thermal/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o | ||
2 | obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o | ||
3 | obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o | ||
4 | obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o | ||
diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c new file mode 100644 index 000000000000..0d8db808f0ae --- /dev/null +++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c | |||
@@ -0,0 +1,400 @@ | |||
1 | /* acpi_thermal_rel.c driver for exporting ACPI thermal relationship | ||
2 | * | ||
3 | * Copyright (c) 2014 Intel Corp | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published by | ||
7 | * the Free Software Foundation. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * Two functionalities included: | ||
13 | * 1. Export _TRT, _ART, via misc device interface to the userspace. | ||
14 | * 2. Provide parsing result to kernel drivers | ||
15 | * | ||
16 | */ | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/export.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/io.h> | ||
23 | #include <linux/acpi.h> | ||
24 | #include <linux/uaccess.h> | ||
25 | #include <linux/miscdevice.h> | ||
26 | #include "acpi_thermal_rel.h" | ||
27 | |||
28 | static acpi_handle acpi_thermal_rel_handle; | ||
29 | static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock); | ||
30 | static int acpi_thermal_rel_chrdev_count; /* #times opened */ | ||
31 | static int acpi_thermal_rel_chrdev_exclu; /* already open exclusive? */ | ||
32 | |||
33 | static int acpi_thermal_rel_open(struct inode *inode, struct file *file) | ||
34 | { | ||
35 | spin_lock(&acpi_thermal_rel_chrdev_lock); | ||
36 | if (acpi_thermal_rel_chrdev_exclu || | ||
37 | (acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) { | ||
38 | spin_unlock(&acpi_thermal_rel_chrdev_lock); | ||
39 | return -EBUSY; | ||
40 | } | ||
41 | |||
42 | if (file->f_flags & O_EXCL) | ||
43 | acpi_thermal_rel_chrdev_exclu = 1; | ||
44 | acpi_thermal_rel_chrdev_count++; | ||
45 | |||
46 | spin_unlock(&acpi_thermal_rel_chrdev_lock); | ||
47 | |||
48 | return nonseekable_open(inode, file); | ||
49 | } | ||
50 | |||
51 | static int acpi_thermal_rel_release(struct inode *inode, struct file *file) | ||
52 | { | ||
53 | spin_lock(&acpi_thermal_rel_chrdev_lock); | ||
54 | acpi_thermal_rel_chrdev_count--; | ||
55 | acpi_thermal_rel_chrdev_exclu = 0; | ||
56 | spin_unlock(&acpi_thermal_rel_chrdev_lock); | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling | ||
63 | * | ||
64 | * @handle: ACPI handle of the device contains _TRT | ||
65 | * @art_count: the number of valid entries resulted from parsing _TRT | ||
66 | * @artp: pointer to pointer of array of art entries in parsing result | ||
67 | * @create_dev: whether to create platform devices for target and source | ||
68 | * | ||
69 | */ | ||
70 | int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, | ||
71 | bool create_dev) | ||
72 | { | ||
73 | acpi_status status; | ||
74 | int result = 0; | ||
75 | int i; | ||
76 | int nr_bad_entries = 0; | ||
77 | struct trt *trts; | ||
78 | struct acpi_device *adev; | ||
79 | union acpi_object *p; | ||
80 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
81 | struct acpi_buffer element = { 0, NULL }; | ||
82 | struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" }; | ||
83 | |||
84 | if (!acpi_has_method(handle, "_TRT")) | ||
85 | return 0; | ||
86 | |||
87 | status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer); | ||
88 | if (ACPI_FAILURE(status)) | ||
89 | return -ENODEV; | ||
90 | |||
91 | p = buffer.pointer; | ||
92 | if (!p || (p->type != ACPI_TYPE_PACKAGE)) { | ||
93 | pr_err("Invalid _TRT data\n"); | ||
94 | result = -EFAULT; | ||
95 | goto end; | ||
96 | } | ||
97 | |||
98 | *trt_count = p->package.count; | ||
99 | trts = kzalloc(*trt_count * sizeof(struct trt), GFP_KERNEL); | ||
100 | if (!trts) { | ||
101 | result = -ENOMEM; | ||
102 | goto end; | ||
103 | } | ||
104 | |||
105 | for (i = 0; i < *trt_count; i++) { | ||
106 | struct trt *trt = &trts[i - nr_bad_entries]; | ||
107 | |||
108 | element.length = sizeof(struct trt); | ||
109 | element.pointer = trt; | ||
110 | |||
111 | status = acpi_extract_package(&(p->package.elements[i]), | ||
112 | &trt_format, &element); | ||
113 | if (ACPI_FAILURE(status)) { | ||
114 | nr_bad_entries++; | ||
115 | pr_warn("_TRT package %d is invalid, ignored\n", i); | ||
116 | continue; | ||
117 | } | ||
118 | if (!create_dev) | ||
119 | continue; | ||
120 | |||
121 | result = acpi_bus_get_device(trt->source, &adev); | ||
122 | if (!result) | ||
123 | acpi_create_platform_device(adev); | ||
124 | else | ||
125 | pr_warn("Failed to get source ACPI device\n"); | ||
126 | |||
127 | result = acpi_bus_get_device(trt->target, &adev); | ||
128 | if (!result) | ||
129 | acpi_create_platform_device(adev); | ||
130 | else | ||
131 | pr_warn("Failed to get target ACPI device\n"); | ||
132 | } | ||
133 | |||
134 | *trtp = trts; | ||
135 | /* don't count bad entries */ | ||
136 | *trt_count -= nr_bad_entries; | ||
137 | end: | ||
138 | kfree(buffer.pointer); | ||
139 | return result; | ||
140 | } | ||
141 | EXPORT_SYMBOL(acpi_parse_trt); | ||
142 | |||
143 | /** | ||
144 | * acpi_parse_art - Parse Active Relationship Table _ART | ||
145 | * | ||
146 | * @handle: ACPI handle of the device contains _ART | ||
147 | * @art_count: the number of valid entries resulted from parsing _ART | ||
148 | * @artp: pointer to pointer of array of art entries in parsing result | ||
149 | * @create_dev: whether to create platform devices for target and source | ||
150 | * | ||
151 | */ | ||
152 | int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, | ||
153 | bool create_dev) | ||
154 | { | ||
155 | acpi_status status; | ||
156 | int result = 0; | ||
157 | int i; | ||
158 | int nr_bad_entries = 0; | ||
159 | struct art *arts; | ||
160 | struct acpi_device *adev; | ||
161 | union acpi_object *p; | ||
162 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
163 | struct acpi_buffer element = { 0, NULL }; | ||
164 | struct acpi_buffer art_format = { | ||
165 | sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" }; | ||
166 | |||
167 | if (!acpi_has_method(handle, "_ART")) | ||
168 | return 0; | ||
169 | |||
170 | status = acpi_evaluate_object(handle, "_ART", NULL, &buffer); | ||
171 | if (ACPI_FAILURE(status)) | ||
172 | return -ENODEV; | ||
173 | |||
174 | p = buffer.pointer; | ||
175 | if (!p || (p->type != ACPI_TYPE_PACKAGE)) { | ||
176 | pr_err("Invalid _ART data\n"); | ||
177 | result = -EFAULT; | ||
178 | goto end; | ||
179 | } | ||
180 | |||
181 | /* ignore p->package.elements[0], as this is _ART Revision field */ | ||
182 | *art_count = p->package.count - 1; | ||
183 | arts = kzalloc(*art_count * sizeof(struct art), GFP_KERNEL); | ||
184 | if (!arts) { | ||
185 | result = -ENOMEM; | ||
186 | goto end; | ||
187 | } | ||
188 | |||
189 | for (i = 0; i < *art_count; i++) { | ||
190 | struct art *art = &arts[i - nr_bad_entries]; | ||
191 | |||
192 | element.length = sizeof(struct art); | ||
193 | element.pointer = art; | ||
194 | |||
195 | status = acpi_extract_package(&(p->package.elements[i + 1]), | ||
196 | &art_format, &element); | ||
197 | if (ACPI_FAILURE(status)) { | ||
198 | pr_warn("_ART package %d is invalid, ignored", i); | ||
199 | nr_bad_entries++; | ||
200 | continue; | ||
201 | } | ||
202 | if (!create_dev) | ||
203 | continue; | ||
204 | |||
205 | if (art->source) { | ||
206 | result = acpi_bus_get_device(art->source, &adev); | ||
207 | if (!result) | ||
208 | acpi_create_platform_device(adev); | ||
209 | else | ||
210 | pr_warn("Failed to get source ACPI device\n"); | ||
211 | } | ||
212 | if (art->target) { | ||
213 | result = acpi_bus_get_device(art->target, &adev); | ||
214 | if (!result) | ||
215 | acpi_create_platform_device(adev); | ||
216 | else | ||
217 | pr_warn("Failed to get source ACPI device\n"); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | *artp = arts; | ||
222 | /* don't count bad entries */ | ||
223 | *art_count -= nr_bad_entries; | ||
224 | end: | ||
225 | kfree(buffer.pointer); | ||
226 | return result; | ||
227 | } | ||
228 | EXPORT_SYMBOL(acpi_parse_art); | ||
229 | |||
230 | |||
231 | /* get device name from acpi handle */ | ||
232 | static void get_single_name(acpi_handle handle, char *name) | ||
233 | { | ||
234 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER}; | ||
235 | |||
236 | if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer))) | ||
237 | pr_warn("Failed get name from handle\n"); | ||
238 | else { | ||
239 | memcpy(name, buffer.pointer, ACPI_NAME_SIZE); | ||
240 | kfree(buffer.pointer); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static int fill_art(char __user *ubuf) | ||
245 | { | ||
246 | int i; | ||
247 | int ret; | ||
248 | int count; | ||
249 | int art_len; | ||
250 | struct art *arts = NULL; | ||
251 | union art_object *art_user; | ||
252 | |||
253 | ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false); | ||
254 | if (ret) | ||
255 | goto free_art; | ||
256 | art_len = count * sizeof(union art_object); | ||
257 | art_user = kzalloc(art_len, GFP_KERNEL); | ||
258 | if (!art_user) { | ||
259 | ret = -ENOMEM; | ||
260 | goto free_art; | ||
261 | } | ||
262 | /* now fill in user art data */ | ||
263 | for (i = 0; i < count; i++) { | ||
264 | /* userspace art needs device name instead of acpi reference */ | ||
265 | get_single_name(arts[i].source, art_user[i].source_device); | ||
266 | get_single_name(arts[i].target, art_user[i].target_device); | ||
267 | /* copy the rest int data in addition to source and target */ | ||
268 | memcpy(&art_user[i].weight, &arts[i].weight, | ||
269 | sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2)); | ||
270 | } | ||
271 | |||
272 | if (copy_to_user(ubuf, art_user, art_len)) | ||
273 | ret = -EFAULT; | ||
274 | kfree(art_user); | ||
275 | free_art: | ||
276 | kfree(arts); | ||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | static int fill_trt(char __user *ubuf) | ||
281 | { | ||
282 | int i; | ||
283 | int ret; | ||
284 | int count; | ||
285 | int trt_len; | ||
286 | struct trt *trts = NULL; | ||
287 | union trt_object *trt_user; | ||
288 | |||
289 | ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false); | ||
290 | if (ret) | ||
291 | goto free_trt; | ||
292 | trt_len = count * sizeof(union trt_object); | ||
293 | trt_user = kzalloc(trt_len, GFP_KERNEL); | ||
294 | if (!trt_user) { | ||
295 | ret = -ENOMEM; | ||
296 | goto free_trt; | ||
297 | } | ||
298 | /* now fill in user trt data */ | ||
299 | for (i = 0; i < count; i++) { | ||
300 | /* userspace trt needs device name instead of acpi reference */ | ||
301 | get_single_name(trts[i].source, trt_user[i].source_device); | ||
302 | get_single_name(trts[i].target, trt_user[i].target_device); | ||
303 | trt_user[i].sample_period = trts[i].sample_period; | ||
304 | trt_user[i].influence = trts[i].influence; | ||
305 | } | ||
306 | |||
307 | if (copy_to_user(ubuf, trt_user, trt_len)) | ||
308 | ret = -EFAULT; | ||
309 | kfree(trt_user); | ||
310 | free_trt: | ||
311 | kfree(trts); | ||
312 | return ret; | ||
313 | } | ||
314 | |||
315 | static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd, | ||
316 | unsigned long __arg) | ||
317 | { | ||
318 | int ret = 0; | ||
319 | unsigned long length = 0; | ||
320 | unsigned long count = 0; | ||
321 | char __user *arg = (void __user *)__arg; | ||
322 | struct trt *trts; | ||
323 | struct art *arts; | ||
324 | |||
325 | switch (cmd) { | ||
326 | case ACPI_THERMAL_GET_TRT_COUNT: | ||
327 | ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count, | ||
328 | &trts, false); | ||
329 | kfree(trts); | ||
330 | if (!ret) | ||
331 | return put_user(count, (unsigned long __user *)__arg); | ||
332 | return ret; | ||
333 | case ACPI_THERMAL_GET_TRT_LEN: | ||
334 | ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count, | ||
335 | &trts, false); | ||
336 | kfree(trts); | ||
337 | length = count * sizeof(union trt_object); | ||
338 | if (!ret) | ||
339 | return put_user(length, (unsigned long __user *)__arg); | ||
340 | return ret; | ||
341 | case ACPI_THERMAL_GET_TRT: | ||
342 | return fill_trt(arg); | ||
343 | case ACPI_THERMAL_GET_ART_COUNT: | ||
344 | ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count, | ||
345 | &arts, false); | ||
346 | kfree(arts); | ||
347 | if (!ret) | ||
348 | return put_user(count, (unsigned long __user *)__arg); | ||
349 | return ret; | ||
350 | case ACPI_THERMAL_GET_ART_LEN: | ||
351 | ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count, | ||
352 | &arts, false); | ||
353 | kfree(arts); | ||
354 | length = count * sizeof(union art_object); | ||
355 | if (!ret) | ||
356 | return put_user(length, (unsigned long __user *)__arg); | ||
357 | return ret; | ||
358 | |||
359 | case ACPI_THERMAL_GET_ART: | ||
360 | return fill_art(arg); | ||
361 | |||
362 | default: | ||
363 | return -ENOTTY; | ||
364 | } | ||
365 | } | ||
366 | |||
367 | static const struct file_operations acpi_thermal_rel_fops = { | ||
368 | .owner = THIS_MODULE, | ||
369 | .open = acpi_thermal_rel_open, | ||
370 | .release = acpi_thermal_rel_release, | ||
371 | .unlocked_ioctl = acpi_thermal_rel_ioctl, | ||
372 | .llseek = no_llseek, | ||
373 | }; | ||
374 | |||
375 | static struct miscdevice acpi_thermal_rel_misc_device = { | ||
376 | .minor = MISC_DYNAMIC_MINOR, | ||
377 | "acpi_thermal_rel", | ||
378 | &acpi_thermal_rel_fops | ||
379 | }; | ||
380 | |||
381 | int acpi_thermal_rel_misc_device_add(acpi_handle handle) | ||
382 | { | ||
383 | acpi_thermal_rel_handle = handle; | ||
384 | |||
385 | return misc_register(&acpi_thermal_rel_misc_device); | ||
386 | } | ||
387 | EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add); | ||
388 | |||
389 | int acpi_thermal_rel_misc_device_remove(acpi_handle handle) | ||
390 | { | ||
391 | misc_deregister(&acpi_thermal_rel_misc_device); | ||
392 | |||
393 | return 0; | ||
394 | } | ||
395 | EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove); | ||
396 | |||
397 | MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); | ||
398 | MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com"); | ||
399 | MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver"); | ||
400 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.h b/drivers/thermal/int340x_thermal/acpi_thermal_rel.h new file mode 100644 index 000000000000..f00700bc9d79 --- /dev/null +++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.h | |||
@@ -0,0 +1,84 @@ | |||
1 | #ifndef __ACPI_ACPI_THERMAL_H | ||
2 | #define __ACPI_ACPI_THERMAL_H | ||
3 | |||
4 | #include <asm/ioctl.h> | ||
5 | |||
6 | #define ACPI_THERMAL_MAGIC 's' | ||
7 | |||
8 | #define ACPI_THERMAL_GET_TRT_LEN _IOR(ACPI_THERMAL_MAGIC, 1, unsigned long) | ||
9 | #define ACPI_THERMAL_GET_ART_LEN _IOR(ACPI_THERMAL_MAGIC, 2, unsigned long) | ||
10 | #define ACPI_THERMAL_GET_TRT_COUNT _IOR(ACPI_THERMAL_MAGIC, 3, unsigned long) | ||
11 | #define ACPI_THERMAL_GET_ART_COUNT _IOR(ACPI_THERMAL_MAGIC, 4, unsigned long) | ||
12 | |||
13 | #define ACPI_THERMAL_GET_TRT _IOR(ACPI_THERMAL_MAGIC, 5, unsigned long) | ||
14 | #define ACPI_THERMAL_GET_ART _IOR(ACPI_THERMAL_MAGIC, 6, unsigned long) | ||
15 | |||
16 | struct art { | ||
17 | acpi_handle source; | ||
18 | acpi_handle target; | ||
19 | u64 weight; | ||
20 | u64 ac0_max; | ||
21 | u64 ac1_max; | ||
22 | u64 ac2_max; | ||
23 | u64 ac3_max; | ||
24 | u64 ac4_max; | ||
25 | u64 ac5_max; | ||
26 | u64 ac6_max; | ||
27 | u64 ac7_max; | ||
28 | u64 ac8_max; | ||
29 | u64 ac9_max; | ||
30 | } __packed; | ||
31 | |||
32 | struct trt { | ||
33 | acpi_handle source; | ||
34 | acpi_handle target; | ||
35 | u64 influence; | ||
36 | u64 sample_period; | ||
37 | u64 reverved1; | ||
38 | u64 reverved2; | ||
39 | u64 reverved3; | ||
40 | u64 reverved4; | ||
41 | } __packed; | ||
42 | |||
43 | #define ACPI_NR_ART_ELEMENTS 13 | ||
44 | /* for usrspace */ | ||
45 | union art_object { | ||
46 | struct { | ||
47 | char source_device[8]; /* ACPI single name */ | ||
48 | char target_device[8]; /* ACPI single name */ | ||
49 | u64 weight; | ||
50 | u64 ac0_max_level; | ||
51 | u64 ac1_max_level; | ||
52 | u64 ac2_max_level; | ||
53 | u64 ac3_max_level; | ||
54 | u64 ac4_max_level; | ||
55 | u64 ac5_max_level; | ||
56 | u64 ac6_max_level; | ||
57 | u64 ac7_max_level; | ||
58 | u64 ac8_max_level; | ||
59 | u64 ac9_max_level; | ||
60 | }; | ||
61 | u64 __data[ACPI_NR_ART_ELEMENTS]; | ||
62 | }; | ||
63 | |||
64 | union trt_object { | ||
65 | struct { | ||
66 | char source_device[8]; /* ACPI single name */ | ||
67 | char target_device[8]; /* ACPI single name */ | ||
68 | u64 influence; | ||
69 | u64 sample_period; | ||
70 | u64 reserved[4]; | ||
71 | }; | ||
72 | u64 __data[8]; | ||
73 | }; | ||
74 | |||
75 | #ifdef __KERNEL__ | ||
76 | int acpi_thermal_rel_misc_device_add(acpi_handle handle); | ||
77 | int acpi_thermal_rel_misc_device_remove(acpi_handle handle); | ||
78 | int acpi_parse_art(acpi_handle handle, int *art_count, struct art **arts, | ||
79 | bool create_dev); | ||
80 | int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trts, | ||
81 | bool create_dev); | ||
82 | #endif | ||
83 | |||
84 | #endif /* __ACPI_ACPI_THERMAL_H */ | ||
diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c new file mode 100644 index 000000000000..edc1cce117ba --- /dev/null +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c | |||
@@ -0,0 +1,271 @@ | |||
1 | /* | ||
2 | * INT3400 thermal driver | ||
3 | * | ||
4 | * Copyright (C) 2014, Intel Corporation | ||
5 | * Authors: Zhang Rui <rui.zhang@intel.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/acpi.h> | ||
16 | #include <linux/thermal.h> | ||
17 | #include "acpi_thermal_rel.h" | ||
18 | |||
19 | enum int3400_thermal_uuid { | ||
20 | INT3400_THERMAL_PASSIVE_1, | ||
21 | INT3400_THERMAL_PASSIVE_2, | ||
22 | INT3400_THERMAL_ACTIVE, | ||
23 | INT3400_THERMAL_CRITICAL, | ||
24 | INT3400_THERMAL_COOLING_MODE, | ||
25 | INT3400_THERMAL_MAXIMUM_UUID, | ||
26 | }; | ||
27 | |||
28 | static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { | ||
29 | "42A441D6-AE6A-462b-A84B-4A8CE79027D3", | ||
30 | "9E04115A-AE87-4D1C-9500-0F3E340BFE75", | ||
31 | "3A95C389-E4B8-4629-A526-C52C88626BAE", | ||
32 | "97C68AE7-15FA-499c-B8C9-5DA81D606E0A", | ||
33 | "16CAF1B7-DD38-40ed-B1C1-1B8A1913D531", | ||
34 | }; | ||
35 | |||
36 | struct int3400_thermal_priv { | ||
37 | struct acpi_device *adev; | ||
38 | struct thermal_zone_device *thermal; | ||
39 | int mode; | ||
40 | int art_count; | ||
41 | struct art *arts; | ||
42 | int trt_count; | ||
43 | struct trt *trts; | ||
44 | u8 uuid_bitmap; | ||
45 | int rel_misc_dev_res; | ||
46 | }; | ||
47 | |||
48 | static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) | ||
49 | { | ||
50 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL}; | ||
51 | union acpi_object *obja, *objb; | ||
52 | int i, j; | ||
53 | int result = 0; | ||
54 | acpi_status status; | ||
55 | |||
56 | status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf); | ||
57 | if (ACPI_FAILURE(status)) | ||
58 | return -ENODEV; | ||
59 | |||
60 | obja = (union acpi_object *)buf.pointer; | ||
61 | if (obja->type != ACPI_TYPE_PACKAGE) { | ||
62 | result = -EINVAL; | ||
63 | goto end; | ||
64 | } | ||
65 | |||
66 | for (i = 0; i < obja->package.count; i++) { | ||
67 | objb = &obja->package.elements[i]; | ||
68 | if (objb->type != ACPI_TYPE_BUFFER) { | ||
69 | result = -EINVAL; | ||
70 | goto end; | ||
71 | } | ||
72 | |||
73 | /* UUID must be 16 bytes */ | ||
74 | if (objb->buffer.length != 16) { | ||
75 | result = -EINVAL; | ||
76 | goto end; | ||
77 | } | ||
78 | |||
79 | for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) { | ||
80 | u8 uuid[16]; | ||
81 | |||
82 | acpi_str_to_uuid(int3400_thermal_uuids[j], uuid); | ||
83 | if (!strncmp(uuid, objb->buffer.pointer, 16)) { | ||
84 | priv->uuid_bitmap |= (1 << j); | ||
85 | break; | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | |||
90 | end: | ||
91 | kfree(buf.pointer); | ||
92 | return result; | ||
93 | } | ||
94 | |||
95 | static int int3400_thermal_run_osc(acpi_handle handle, | ||
96 | enum int3400_thermal_uuid uuid, bool enable) | ||
97 | { | ||
98 | u32 ret, buf[2]; | ||
99 | acpi_status status; | ||
100 | int result = 0; | ||
101 | struct acpi_osc_context context = { | ||
102 | .uuid_str = int3400_thermal_uuids[uuid], | ||
103 | .rev = 1, | ||
104 | .cap.length = 8, | ||
105 | }; | ||
106 | |||
107 | buf[OSC_QUERY_DWORD] = 0; | ||
108 | buf[OSC_SUPPORT_DWORD] = enable; | ||
109 | |||
110 | context.cap.pointer = buf; | ||
111 | |||
112 | status = acpi_run_osc(handle, &context); | ||
113 | if (ACPI_SUCCESS(status)) { | ||
114 | ret = *((u32 *)(context.ret.pointer + 4)); | ||
115 | if (ret != enable) | ||
116 | result = -EPERM; | ||
117 | } else | ||
118 | result = -EPERM; | ||
119 | |||
120 | kfree(context.ret.pointer); | ||
121 | return result; | ||
122 | } | ||
123 | |||
124 | static int int3400_thermal_get_temp(struct thermal_zone_device *thermal, | ||
125 | unsigned long *temp) | ||
126 | { | ||
127 | *temp = 20 * 1000; /* faked temp sensor with 20C */ | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int int3400_thermal_get_mode(struct thermal_zone_device *thermal, | ||
132 | enum thermal_device_mode *mode) | ||
133 | { | ||
134 | struct int3400_thermal_priv *priv = thermal->devdata; | ||
135 | |||
136 | if (!priv) | ||
137 | return -EINVAL; | ||
138 | |||
139 | *mode = priv->mode; | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int int3400_thermal_set_mode(struct thermal_zone_device *thermal, | ||
145 | enum thermal_device_mode mode) | ||
146 | { | ||
147 | struct int3400_thermal_priv *priv = thermal->devdata; | ||
148 | bool enable; | ||
149 | int result = 0; | ||
150 | |||
151 | if (!priv) | ||
152 | return -EINVAL; | ||
153 | |||
154 | if (mode == THERMAL_DEVICE_ENABLED) | ||
155 | enable = true; | ||
156 | else if (mode == THERMAL_DEVICE_DISABLED) | ||
157 | enable = false; | ||
158 | else | ||
159 | return -EINVAL; | ||
160 | |||
161 | if (enable != priv->mode) { | ||
162 | priv->mode = enable; | ||
163 | /* currently, only PASSIVE COOLING is supported */ | ||
164 | result = int3400_thermal_run_osc(priv->adev->handle, | ||
165 | INT3400_THERMAL_PASSIVE_1, enable); | ||
166 | } | ||
167 | return result; | ||
168 | } | ||
169 | |||
170 | static struct thermal_zone_device_ops int3400_thermal_ops = { | ||
171 | .get_temp = int3400_thermal_get_temp, | ||
172 | }; | ||
173 | |||
174 | static struct thermal_zone_params int3400_thermal_params = { | ||
175 | .governor_name = "user_space", | ||
176 | .no_hwmon = true, | ||
177 | }; | ||
178 | |||
179 | static int int3400_thermal_probe(struct platform_device *pdev) | ||
180 | { | ||
181 | struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); | ||
182 | struct int3400_thermal_priv *priv; | ||
183 | int result; | ||
184 | |||
185 | if (!adev) | ||
186 | return -ENODEV; | ||
187 | |||
188 | priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); | ||
189 | if (!priv) | ||
190 | return -ENOMEM; | ||
191 | |||
192 | priv->adev = adev; | ||
193 | |||
194 | result = int3400_thermal_get_uuids(priv); | ||
195 | if (result) | ||
196 | goto free_priv; | ||
197 | |||
198 | result = acpi_parse_art(priv->adev->handle, &priv->art_count, | ||
199 | &priv->arts, true); | ||
200 | if (result) | ||
201 | goto free_priv; | ||
202 | |||
203 | |||
204 | result = acpi_parse_trt(priv->adev->handle, &priv->trt_count, | ||
205 | &priv->trts, true); | ||
206 | if (result) | ||
207 | goto free_art; | ||
208 | |||
209 | platform_set_drvdata(pdev, priv); | ||
210 | |||
211 | if (priv->uuid_bitmap & 1 << INT3400_THERMAL_PASSIVE_1) { | ||
212 | int3400_thermal_ops.get_mode = int3400_thermal_get_mode; | ||
213 | int3400_thermal_ops.set_mode = int3400_thermal_set_mode; | ||
214 | } | ||
215 | priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0, | ||
216 | priv, &int3400_thermal_ops, | ||
217 | &int3400_thermal_params, 0, 0); | ||
218 | if (IS_ERR(priv->thermal)) { | ||
219 | result = PTR_ERR(priv->thermal); | ||
220 | goto free_trt; | ||
221 | } | ||
222 | |||
223 | priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add( | ||
224 | priv->adev->handle); | ||
225 | |||
226 | return 0; | ||
227 | free_trt: | ||
228 | kfree(priv->trts); | ||
229 | free_art: | ||
230 | kfree(priv->arts); | ||
231 | free_priv: | ||
232 | kfree(priv); | ||
233 | return result; | ||
234 | } | ||
235 | |||
236 | static int int3400_thermal_remove(struct platform_device *pdev) | ||
237 | { | ||
238 | struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); | ||
239 | |||
240 | if (!priv->rel_misc_dev_res) | ||
241 | acpi_thermal_rel_misc_device_remove(priv->adev->handle); | ||
242 | |||
243 | thermal_zone_device_unregister(priv->thermal); | ||
244 | kfree(priv->trts); | ||
245 | kfree(priv->arts); | ||
246 | kfree(priv); | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | static const struct acpi_device_id int3400_thermal_match[] = { | ||
251 | {"INT3400", 0}, | ||
252 | {} | ||
253 | }; | ||
254 | |||
255 | MODULE_DEVICE_TABLE(acpi, int3400_thermal_match); | ||
256 | |||
257 | static struct platform_driver int3400_thermal_driver = { | ||
258 | .probe = int3400_thermal_probe, | ||
259 | .remove = int3400_thermal_remove, | ||
260 | .driver = { | ||
261 | .name = "int3400 thermal", | ||
262 | .owner = THIS_MODULE, | ||
263 | .acpi_match_table = ACPI_PTR(int3400_thermal_match), | ||
264 | }, | ||
265 | }; | ||
266 | |||
267 | module_platform_driver(int3400_thermal_driver); | ||
268 | |||
269 | MODULE_DESCRIPTION("INT3400 Thermal driver"); | ||
270 | MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); | ||
271 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/thermal/int340x_thermal/int3402_thermal.c b/drivers/thermal/int340x_thermal/int3402_thermal.c new file mode 100644 index 000000000000..a5d08c14ba24 --- /dev/null +++ b/drivers/thermal/int340x_thermal/int3402_thermal.c | |||
@@ -0,0 +1,242 @@ | |||
1 | /* | ||
2 | * INT3402 thermal driver for memory temperature reporting | ||
3 | * | ||
4 | * Copyright (C) 2014, Intel Corporation | ||
5 | * Authors: Aaron Lu <aaron.lu@intel.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/acpi.h> | ||
16 | #include <linux/thermal.h> | ||
17 | |||
18 | #define ACPI_ACTIVE_COOLING_MAX_NR 10 | ||
19 | |||
20 | struct active_trip { | ||
21 | unsigned long temp; | ||
22 | int id; | ||
23 | bool valid; | ||
24 | }; | ||
25 | |||
26 | struct int3402_thermal_data { | ||
27 | unsigned long *aux_trips; | ||
28 | int aux_trip_nr; | ||
29 | unsigned long psv_temp; | ||
30 | int psv_trip_id; | ||
31 | unsigned long crt_temp; | ||
32 | int crt_trip_id; | ||
33 | unsigned long hot_temp; | ||
34 | int hot_trip_id; | ||
35 | struct active_trip act_trips[ACPI_ACTIVE_COOLING_MAX_NR]; | ||
36 | acpi_handle *handle; | ||
37 | }; | ||
38 | |||
39 | static int int3402_thermal_get_zone_temp(struct thermal_zone_device *zone, | ||
40 | unsigned long *temp) | ||
41 | { | ||
42 | struct int3402_thermal_data *d = zone->devdata; | ||
43 | unsigned long long tmp; | ||
44 | acpi_status status; | ||
45 | |||
46 | status = acpi_evaluate_integer(d->handle, "_TMP", NULL, &tmp); | ||
47 | if (ACPI_FAILURE(status)) | ||
48 | return -ENODEV; | ||
49 | |||
50 | /* _TMP returns the temperature in tenths of degrees Kelvin */ | ||
51 | *temp = DECI_KELVIN_TO_MILLICELSIUS(tmp); | ||
52 | |||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | static int int3402_thermal_get_trip_temp(struct thermal_zone_device *zone, | ||
57 | int trip, unsigned long *temp) | ||
58 | { | ||
59 | struct int3402_thermal_data *d = zone->devdata; | ||
60 | int i; | ||
61 | |||
62 | if (trip < d->aux_trip_nr) | ||
63 | *temp = d->aux_trips[trip]; | ||
64 | else if (trip == d->crt_trip_id) | ||
65 | *temp = d->crt_temp; | ||
66 | else if (trip == d->psv_trip_id) | ||
67 | *temp = d->psv_temp; | ||
68 | else if (trip == d->hot_trip_id) | ||
69 | *temp = d->hot_temp; | ||
70 | else { | ||
71 | for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { | ||
72 | if (d->act_trips[i].valid && | ||
73 | d->act_trips[i].id == trip) { | ||
74 | *temp = d->act_trips[i].temp; | ||
75 | break; | ||
76 | } | ||
77 | } | ||
78 | if (i == ACPI_ACTIVE_COOLING_MAX_NR) | ||
79 | return -EINVAL; | ||
80 | } | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static int int3402_thermal_get_trip_type(struct thermal_zone_device *zone, | ||
85 | int trip, enum thermal_trip_type *type) | ||
86 | { | ||
87 | struct int3402_thermal_data *d = zone->devdata; | ||
88 | int i; | ||
89 | |||
90 | if (trip < d->aux_trip_nr) | ||
91 | *type = THERMAL_TRIP_PASSIVE; | ||
92 | else if (trip == d->crt_trip_id) | ||
93 | *type = THERMAL_TRIP_CRITICAL; | ||
94 | else if (trip == d->hot_trip_id) | ||
95 | *type = THERMAL_TRIP_HOT; | ||
96 | else if (trip == d->psv_trip_id) | ||
97 | *type = THERMAL_TRIP_PASSIVE; | ||
98 | else { | ||
99 | for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { | ||
100 | if (d->act_trips[i].valid && | ||
101 | d->act_trips[i].id == trip) { | ||
102 | *type = THERMAL_TRIP_ACTIVE; | ||
103 | break; | ||
104 | } | ||
105 | } | ||
106 | if (i == ACPI_ACTIVE_COOLING_MAX_NR) | ||
107 | return -EINVAL; | ||
108 | } | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int int3402_thermal_set_trip_temp(struct thermal_zone_device *zone, int trip, | ||
113 | unsigned long temp) | ||
114 | { | ||
115 | struct int3402_thermal_data *d = zone->devdata; | ||
116 | acpi_status status; | ||
117 | char name[10]; | ||
118 | |||
119 | snprintf(name, sizeof(name), "PAT%d", trip); | ||
120 | status = acpi_execute_simple_method(d->handle, name, | ||
121 | MILLICELSIUS_TO_DECI_KELVIN(temp)); | ||
122 | if (ACPI_FAILURE(status)) | ||
123 | return -EIO; | ||
124 | |||
125 | d->aux_trips[trip] = temp; | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | static struct thermal_zone_device_ops int3402_thermal_zone_ops = { | ||
130 | .get_temp = int3402_thermal_get_zone_temp, | ||
131 | .get_trip_temp = int3402_thermal_get_trip_temp, | ||
132 | .get_trip_type = int3402_thermal_get_trip_type, | ||
133 | .set_trip_temp = int3402_thermal_set_trip_temp, | ||
134 | }; | ||
135 | |||
136 | static struct thermal_zone_params int3402_thermal_params = { | ||
137 | .governor_name = "user_space", | ||
138 | .no_hwmon = true, | ||
139 | }; | ||
140 | |||
141 | static int int3402_thermal_get_temp(acpi_handle handle, char *name, | ||
142 | unsigned long *temp) | ||
143 | { | ||
144 | unsigned long long r; | ||
145 | acpi_status status; | ||
146 | |||
147 | status = acpi_evaluate_integer(handle, name, NULL, &r); | ||
148 | if (ACPI_FAILURE(status)) | ||
149 | return -EIO; | ||
150 | |||
151 | *temp = DECI_KELVIN_TO_MILLICELSIUS(r); | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static int int3402_thermal_probe(struct platform_device *pdev) | ||
156 | { | ||
157 | struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); | ||
158 | struct int3402_thermal_data *d; | ||
159 | struct thermal_zone_device *zone; | ||
160 | acpi_status status; | ||
161 | unsigned long long trip_cnt; | ||
162 | int trip_mask = 0, i; | ||
163 | |||
164 | if (!acpi_has_method(adev->handle, "_TMP")) | ||
165 | return -ENODEV; | ||
166 | |||
167 | d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL); | ||
168 | if (!d) | ||
169 | return -ENOMEM; | ||
170 | |||
171 | status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt); | ||
172 | if (ACPI_FAILURE(status)) | ||
173 | trip_cnt = 0; | ||
174 | else { | ||
175 | d->aux_trips = devm_kzalloc(&pdev->dev, | ||
176 | sizeof(*d->aux_trips) * trip_cnt, GFP_KERNEL); | ||
177 | if (!d->aux_trips) | ||
178 | return -ENOMEM; | ||
179 | trip_mask = trip_cnt - 1; | ||
180 | d->handle = adev->handle; | ||
181 | d->aux_trip_nr = trip_cnt; | ||
182 | } | ||
183 | |||
184 | d->crt_trip_id = -1; | ||
185 | if (!int3402_thermal_get_temp(adev->handle, "_CRT", &d->crt_temp)) | ||
186 | d->crt_trip_id = trip_cnt++; | ||
187 | d->hot_trip_id = -1; | ||
188 | if (!int3402_thermal_get_temp(adev->handle, "_HOT", &d->hot_temp)) | ||
189 | d->hot_trip_id = trip_cnt++; | ||
190 | d->psv_trip_id = -1; | ||
191 | if (!int3402_thermal_get_temp(adev->handle, "_PSV", &d->psv_temp)) | ||
192 | d->psv_trip_id = trip_cnt++; | ||
193 | for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) { | ||
194 | char name[5] = { '_', 'A', 'C', '0' + i, '\0' }; | ||
195 | if (int3402_thermal_get_temp(adev->handle, name, | ||
196 | &d->act_trips[i].temp)) | ||
197 | break; | ||
198 | d->act_trips[i].id = trip_cnt++; | ||
199 | d->act_trips[i].valid = true; | ||
200 | } | ||
201 | |||
202 | zone = thermal_zone_device_register(acpi_device_bid(adev), trip_cnt, | ||
203 | trip_mask, d, | ||
204 | &int3402_thermal_zone_ops, | ||
205 | &int3402_thermal_params, | ||
206 | 0, 0); | ||
207 | if (IS_ERR(zone)) | ||
208 | return PTR_ERR(zone); | ||
209 | platform_set_drvdata(pdev, zone); | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static int int3402_thermal_remove(struct platform_device *pdev) | ||
215 | { | ||
216 | struct thermal_zone_device *zone = platform_get_drvdata(pdev); | ||
217 | |||
218 | thermal_zone_device_unregister(zone); | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | static const struct acpi_device_id int3402_thermal_match[] = { | ||
223 | {"INT3402", 0}, | ||
224 | {} | ||
225 | }; | ||
226 | |||
227 | MODULE_DEVICE_TABLE(acpi, int3402_thermal_match); | ||
228 | |||
229 | static struct platform_driver int3402_thermal_driver = { | ||
230 | .probe = int3402_thermal_probe, | ||
231 | .remove = int3402_thermal_remove, | ||
232 | .driver = { | ||
233 | .name = "int3402 thermal", | ||
234 | .owner = THIS_MODULE, | ||
235 | .acpi_match_table = int3402_thermal_match, | ||
236 | }, | ||
237 | }; | ||
238 | |||
239 | module_platform_driver(int3402_thermal_driver); | ||
240 | |||
241 | MODULE_DESCRIPTION("INT3402 Thermal driver"); | ||
242 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/int340x_thermal/int3403_thermal.c new file mode 100644 index 000000000000..d20dba986f0f --- /dev/null +++ b/drivers/thermal/int340x_thermal/int3403_thermal.c | |||
@@ -0,0 +1,477 @@ | |||
1 | /* | ||
2 | * ACPI INT3403 thermal driver | ||
3 | * Copyright (c) 2013, Intel Corporation. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/acpi.h> | ||
20 | #include <linux/thermal.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | |||
23 | #define INT3403_TYPE_SENSOR 0x03 | ||
24 | #define INT3403_TYPE_CHARGER 0x0B | ||
25 | #define INT3403_TYPE_BATTERY 0x0C | ||
26 | #define INT3403_PERF_CHANGED_EVENT 0x80 | ||
27 | #define INT3403_THERMAL_EVENT 0x90 | ||
28 | |||
29 | #define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100) | ||
30 | #define KELVIN_OFFSET 2732 | ||
31 | #define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off)) | ||
32 | |||
33 | struct int3403_sensor { | ||
34 | struct thermal_zone_device *tzone; | ||
35 | unsigned long *thresholds; | ||
36 | unsigned long crit_temp; | ||
37 | int crit_trip_id; | ||
38 | unsigned long psv_temp; | ||
39 | int psv_trip_id; | ||
40 | |||
41 | }; | ||
42 | |||
43 | struct int3403_performance_state { | ||
44 | u64 performance; | ||
45 | u64 power; | ||
46 | u64 latency; | ||
47 | u64 linear; | ||
48 | u64 control; | ||
49 | u64 raw_performace; | ||
50 | char *raw_unit; | ||
51 | int reserved; | ||
52 | }; | ||
53 | |||
54 | struct int3403_cdev { | ||
55 | struct thermal_cooling_device *cdev; | ||
56 | unsigned long max_state; | ||
57 | }; | ||
58 | |||
59 | struct int3403_priv { | ||
60 | struct platform_device *pdev; | ||
61 | struct acpi_device *adev; | ||
62 | unsigned long long type; | ||
63 | void *priv; | ||
64 | }; | ||
65 | |||
66 | static int sys_get_curr_temp(struct thermal_zone_device *tzone, | ||
67 | unsigned long *temp) | ||
68 | { | ||
69 | struct int3403_priv *priv = tzone->devdata; | ||
70 | struct acpi_device *device = priv->adev; | ||
71 | unsigned long long tmp; | ||
72 | acpi_status status; | ||
73 | |||
74 | status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp); | ||
75 | if (ACPI_FAILURE(status)) | ||
76 | return -EIO; | ||
77 | |||
78 | *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int sys_get_trip_hyst(struct thermal_zone_device *tzone, | ||
84 | int trip, unsigned long *temp) | ||
85 | { | ||
86 | struct int3403_priv *priv = tzone->devdata; | ||
87 | struct acpi_device *device = priv->adev; | ||
88 | unsigned long long hyst; | ||
89 | acpi_status status; | ||
90 | |||
91 | status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst); | ||
92 | if (ACPI_FAILURE(status)) | ||
93 | return -EIO; | ||
94 | |||
95 | *temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static int sys_get_trip_temp(struct thermal_zone_device *tzone, | ||
101 | int trip, unsigned long *temp) | ||
102 | { | ||
103 | struct int3403_priv *priv = tzone->devdata; | ||
104 | struct int3403_sensor *obj = priv->priv; | ||
105 | |||
106 | if (priv->type != INT3403_TYPE_SENSOR || !obj) | ||
107 | return -EINVAL; | ||
108 | |||
109 | if (trip == obj->crit_trip_id) | ||
110 | *temp = obj->crit_temp; | ||
111 | else if (trip == obj->psv_trip_id) | ||
112 | *temp = obj->psv_temp; | ||
113 | else { | ||
114 | /* | ||
115 | * get_trip_temp is a mandatory callback but | ||
116 | * PATx method doesn't return any value, so return | ||
117 | * cached value, which was last set from user space | ||
118 | */ | ||
119 | *temp = obj->thresholds[trip]; | ||
120 | } | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static int sys_get_trip_type(struct thermal_zone_device *thermal, | ||
126 | int trip, enum thermal_trip_type *type) | ||
127 | { | ||
128 | struct int3403_priv *priv = thermal->devdata; | ||
129 | struct int3403_sensor *obj = priv->priv; | ||
130 | |||
131 | /* Mandatory callback, may not mean much here */ | ||
132 | if (trip == obj->crit_trip_id) | ||
133 | *type = THERMAL_TRIP_CRITICAL; | ||
134 | else | ||
135 | *type = THERMAL_TRIP_PASSIVE; | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip, | ||
141 | unsigned long temp) | ||
142 | { | ||
143 | struct int3403_priv *priv = tzone->devdata; | ||
144 | struct acpi_device *device = priv->adev; | ||
145 | struct int3403_sensor *obj = priv->priv; | ||
146 | acpi_status status; | ||
147 | char name[10]; | ||
148 | int ret = 0; | ||
149 | |||
150 | snprintf(name, sizeof(name), "PAT%d", trip); | ||
151 | if (acpi_has_method(device->handle, name)) { | ||
152 | status = acpi_execute_simple_method(device->handle, name, | ||
153 | MILLI_CELSIUS_TO_DECI_KELVIN(temp, | ||
154 | KELVIN_OFFSET)); | ||
155 | if (ACPI_FAILURE(status)) | ||
156 | ret = -EIO; | ||
157 | else | ||
158 | obj->thresholds[trip] = temp; | ||
159 | } else { | ||
160 | ret = -EIO; | ||
161 | dev_err(&device->dev, "sys_set_trip_temp: method not found\n"); | ||
162 | } | ||
163 | |||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static struct thermal_zone_device_ops tzone_ops = { | ||
168 | .get_temp = sys_get_curr_temp, | ||
169 | .get_trip_temp = sys_get_trip_temp, | ||
170 | .get_trip_type = sys_get_trip_type, | ||
171 | .set_trip_temp = sys_set_trip_temp, | ||
172 | .get_trip_hyst = sys_get_trip_hyst, | ||
173 | }; | ||
174 | |||
175 | static struct thermal_zone_params int3403_thermal_params = { | ||
176 | .governor_name = "user_space", | ||
177 | .no_hwmon = true, | ||
178 | }; | ||
179 | |||
180 | static void int3403_notify(acpi_handle handle, | ||
181 | u32 event, void *data) | ||
182 | { | ||
183 | struct int3403_priv *priv = data; | ||
184 | struct int3403_sensor *obj; | ||
185 | |||
186 | if (!priv) | ||
187 | return; | ||
188 | |||
189 | obj = priv->priv; | ||
190 | if (priv->type != INT3403_TYPE_SENSOR || !obj) | ||
191 | return; | ||
192 | |||
193 | switch (event) { | ||
194 | case INT3403_PERF_CHANGED_EVENT: | ||
195 | break; | ||
196 | case INT3403_THERMAL_EVENT: | ||
197 | thermal_zone_device_update(obj->tzone); | ||
198 | break; | ||
199 | default: | ||
200 | dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event); | ||
201 | break; | ||
202 | } | ||
203 | } | ||
204 | |||
205 | static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp) | ||
206 | { | ||
207 | unsigned long long crt; | ||
208 | acpi_status status; | ||
209 | |||
210 | status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt); | ||
211 | if (ACPI_FAILURE(status)) | ||
212 | return -EIO; | ||
213 | |||
214 | *temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp) | ||
220 | { | ||
221 | unsigned long long psv; | ||
222 | acpi_status status; | ||
223 | |||
224 | status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv); | ||
225 | if (ACPI_FAILURE(status)) | ||
226 | return -EIO; | ||
227 | |||
228 | *temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET); | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static int int3403_sensor_add(struct int3403_priv *priv) | ||
234 | { | ||
235 | int result = 0; | ||
236 | acpi_status status; | ||
237 | struct int3403_sensor *obj; | ||
238 | unsigned long long trip_cnt; | ||
239 | int trip_mask = 0; | ||
240 | |||
241 | obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL); | ||
242 | if (!obj) | ||
243 | return -ENOMEM; | ||
244 | |||
245 | priv->priv = obj; | ||
246 | |||
247 | status = acpi_evaluate_integer(priv->adev->handle, "PATC", NULL, | ||
248 | &trip_cnt); | ||
249 | if (ACPI_FAILURE(status)) | ||
250 | trip_cnt = 0; | ||
251 | |||
252 | if (trip_cnt) { | ||
253 | /* We have to cache, thresholds can't be readback */ | ||
254 | obj->thresholds = devm_kzalloc(&priv->pdev->dev, | ||
255 | sizeof(*obj->thresholds) * trip_cnt, | ||
256 | GFP_KERNEL); | ||
257 | if (!obj->thresholds) { | ||
258 | result = -ENOMEM; | ||
259 | goto err_free_obj; | ||
260 | } | ||
261 | trip_mask = BIT(trip_cnt) - 1; | ||
262 | } | ||
263 | |||
264 | obj->psv_trip_id = -1; | ||
265 | if (!sys_get_trip_psv(priv->adev, &obj->psv_temp)) | ||
266 | obj->psv_trip_id = trip_cnt++; | ||
267 | |||
268 | obj->crit_trip_id = -1; | ||
269 | if (!sys_get_trip_crt(priv->adev, &obj->crit_temp)) | ||
270 | obj->crit_trip_id = trip_cnt++; | ||
271 | |||
272 | obj->tzone = thermal_zone_device_register(acpi_device_bid(priv->adev), | ||
273 | trip_cnt, trip_mask, priv, &tzone_ops, | ||
274 | &int3403_thermal_params, 0, 0); | ||
275 | if (IS_ERR(obj->tzone)) { | ||
276 | result = PTR_ERR(obj->tzone); | ||
277 | obj->tzone = NULL; | ||
278 | goto err_free_obj; | ||
279 | } | ||
280 | |||
281 | result = acpi_install_notify_handler(priv->adev->handle, | ||
282 | ACPI_DEVICE_NOTIFY, int3403_notify, | ||
283 | (void *)priv); | ||
284 | if (result) | ||
285 | goto err_free_obj; | ||
286 | |||
287 | return 0; | ||
288 | |||
289 | err_free_obj: | ||
290 | if (obj->tzone) | ||
291 | thermal_zone_device_unregister(obj->tzone); | ||
292 | return result; | ||
293 | } | ||
294 | |||
295 | static int int3403_sensor_remove(struct int3403_priv *priv) | ||
296 | { | ||
297 | struct int3403_sensor *obj = priv->priv; | ||
298 | |||
299 | thermal_zone_device_unregister(obj->tzone); | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | /* INT3403 Cooling devices */ | ||
304 | static int int3403_get_max_state(struct thermal_cooling_device *cdev, | ||
305 | unsigned long *state) | ||
306 | { | ||
307 | struct int3403_priv *priv = cdev->devdata; | ||
308 | struct int3403_cdev *obj = priv->priv; | ||
309 | |||
310 | *state = obj->max_state; | ||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | static int int3403_get_cur_state(struct thermal_cooling_device *cdev, | ||
315 | unsigned long *state) | ||
316 | { | ||
317 | struct int3403_priv *priv = cdev->devdata; | ||
318 | unsigned long long level; | ||
319 | acpi_status status; | ||
320 | |||
321 | status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level); | ||
322 | if (ACPI_SUCCESS(status)) { | ||
323 | *state = level; | ||
324 | return 0; | ||
325 | } else | ||
326 | return -EINVAL; | ||
327 | } | ||
328 | |||
329 | static int | ||
330 | int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) | ||
331 | { | ||
332 | struct int3403_priv *priv = cdev->devdata; | ||
333 | acpi_status status; | ||
334 | |||
335 | status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state); | ||
336 | if (ACPI_SUCCESS(status)) | ||
337 | return 0; | ||
338 | else | ||
339 | return -EINVAL; | ||
340 | } | ||
341 | |||
342 | static const struct thermal_cooling_device_ops int3403_cooling_ops = { | ||
343 | .get_max_state = int3403_get_max_state, | ||
344 | .get_cur_state = int3403_get_cur_state, | ||
345 | .set_cur_state = int3403_set_cur_state, | ||
346 | }; | ||
347 | |||
348 | static int int3403_cdev_add(struct int3403_priv *priv) | ||
349 | { | ||
350 | int result = 0; | ||
351 | acpi_status status; | ||
352 | struct int3403_cdev *obj; | ||
353 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
354 | union acpi_object *p; | ||
355 | |||
356 | obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL); | ||
357 | if (!obj) | ||
358 | return -ENOMEM; | ||
359 | |||
360 | status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf); | ||
361 | if (ACPI_FAILURE(status)) | ||
362 | return -ENODEV; | ||
363 | |||
364 | p = buf.pointer; | ||
365 | if (!p || (p->type != ACPI_TYPE_PACKAGE)) { | ||
366 | printk(KERN_WARNING "Invalid PPSS data\n"); | ||
367 | return -EFAULT; | ||
368 | } | ||
369 | |||
370 | obj->max_state = p->package.count - 1; | ||
371 | obj->cdev = | ||
372 | thermal_cooling_device_register(acpi_device_bid(priv->adev), | ||
373 | priv, &int3403_cooling_ops); | ||
374 | if (IS_ERR(obj->cdev)) | ||
375 | result = PTR_ERR(obj->cdev); | ||
376 | |||
377 | priv->priv = obj; | ||
378 | |||
379 | /* TODO: add ACPI notification support */ | ||
380 | |||
381 | return result; | ||
382 | } | ||
383 | |||
384 | static int int3403_cdev_remove(struct int3403_priv *priv) | ||
385 | { | ||
386 | struct int3403_cdev *obj = priv->priv; | ||
387 | |||
388 | thermal_cooling_device_unregister(obj->cdev); | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | static int int3403_add(struct platform_device *pdev) | ||
393 | { | ||
394 | struct int3403_priv *priv; | ||
395 | int result = 0; | ||
396 | acpi_status status; | ||
397 | |||
398 | priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv), | ||
399 | GFP_KERNEL); | ||
400 | if (!priv) | ||
401 | return -ENOMEM; | ||
402 | |||
403 | priv->pdev = pdev; | ||
404 | priv->adev = ACPI_COMPANION(&(pdev->dev)); | ||
405 | if (!priv->adev) { | ||
406 | result = -EINVAL; | ||
407 | goto err; | ||
408 | } | ||
409 | |||
410 | status = acpi_evaluate_integer(priv->adev->handle, "PTYP", | ||
411 | NULL, &priv->type); | ||
412 | if (ACPI_FAILURE(status)) { | ||
413 | result = -EINVAL; | ||
414 | goto err; | ||
415 | } | ||
416 | |||
417 | platform_set_drvdata(pdev, priv); | ||
418 | switch (priv->type) { | ||
419 | case INT3403_TYPE_SENSOR: | ||
420 | result = int3403_sensor_add(priv); | ||
421 | break; | ||
422 | case INT3403_TYPE_CHARGER: | ||
423 | case INT3403_TYPE_BATTERY: | ||
424 | result = int3403_cdev_add(priv); | ||
425 | break; | ||
426 | default: | ||
427 | result = -EINVAL; | ||
428 | } | ||
429 | |||
430 | if (result) | ||
431 | goto err; | ||
432 | return result; | ||
433 | |||
434 | err: | ||
435 | return result; | ||
436 | } | ||
437 | |||
438 | static int int3403_remove(struct platform_device *pdev) | ||
439 | { | ||
440 | struct int3403_priv *priv = platform_get_drvdata(pdev); | ||
441 | |||
442 | switch (priv->type) { | ||
443 | case INT3403_TYPE_SENSOR: | ||
444 | int3403_sensor_remove(priv); | ||
445 | break; | ||
446 | case INT3403_TYPE_CHARGER: | ||
447 | case INT3403_TYPE_BATTERY: | ||
448 | int3403_cdev_remove(priv); | ||
449 | break; | ||
450 | default: | ||
451 | break; | ||
452 | } | ||
453 | |||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | static const struct acpi_device_id int3403_device_ids[] = { | ||
458 | {"INT3403", 0}, | ||
459 | {"", 0}, | ||
460 | }; | ||
461 | MODULE_DEVICE_TABLE(acpi, int3403_device_ids); | ||
462 | |||
463 | static struct platform_driver int3403_driver = { | ||
464 | .probe = int3403_add, | ||
465 | .remove = int3403_remove, | ||
466 | .driver = { | ||
467 | .name = "int3403 thermal", | ||
468 | .owner = THIS_MODULE, | ||
469 | .acpi_match_table = int3403_device_ids, | ||
470 | }, | ||
471 | }; | ||
472 | |||
473 | module_platform_driver(int3403_driver); | ||
474 | |||
475 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); | ||
476 | MODULE_LICENSE("GPL v2"); | ||
477 | MODULE_DESCRIPTION("ACPI INT3403 thermal driver"); | ||
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 4b2b999b7611..f8eb625b8400 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c | |||
@@ -401,6 +401,10 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, | |||
401 | struct of_phandle_args sensor_specs; | 401 | struct of_phandle_args sensor_specs; |
402 | int ret, id; | 402 | int ret, id; |
403 | 403 | ||
404 | /* Check whether child is enabled or not */ | ||
405 | if (!of_device_is_available(child)) | ||
406 | continue; | ||
407 | |||
404 | /* For now, thermal framework supports only 1 sensor per zone */ | 408 | /* For now, thermal framework supports only 1 sensor per zone */ |
405 | ret = of_parse_phandle_with_args(child, "thermal-sensors", | 409 | ret = of_parse_phandle_with_args(child, "thermal-sensors", |
406 | "#thermal-sensor-cells", | 410 | "#thermal-sensor-cells", |
@@ -771,6 +775,10 @@ int __init of_parse_thermal_zones(void) | |||
771 | struct thermal_zone_device *zone; | 775 | struct thermal_zone_device *zone; |
772 | struct thermal_zone_params *tzp; | 776 | struct thermal_zone_params *tzp; |
773 | 777 | ||
778 | /* Check whether child is enabled or not */ | ||
779 | if (!of_device_is_available(child)) | ||
780 | continue; | ||
781 | |||
774 | tz = thermal_of_build_thermal_zone(child); | 782 | tz = thermal_of_build_thermal_zone(child); |
775 | if (IS_ERR(tz)) { | 783 | if (IS_ERR(tz)) { |
776 | pr_err("failed to build thermal zone %s: %ld\n", | 784 | pr_err("failed to build thermal zone %s: %ld\n", |
@@ -838,6 +846,10 @@ void of_thermal_destroy_zones(void) | |||
838 | for_each_child_of_node(np, child) { | 846 | for_each_child_of_node(np, child) { |
839 | struct thermal_zone_device *zone; | 847 | struct thermal_zone_device *zone; |
840 | 848 | ||
849 | /* Check whether child is enabled or not */ | ||
850 | if (!of_device_is_available(child)) | ||
851 | continue; | ||
852 | |||
841 | zone = thermal_zone_get_zone_by_name(child->name); | 853 | zone = thermal_zone_get_zone_by_name(child->name); |
842 | if (IS_ERR(zone)) | 854 | if (IS_ERR(zone)) |
843 | continue; | 855 | continue; |
diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index f251521baaa2..fdd1f523a1ed 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c | |||
@@ -23,6 +23,7 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/thermal.h> | 25 | #include <linux/thermal.h> |
26 | #include <trace/events/thermal.h> | ||
26 | 27 | ||
27 | #include "thermal_core.h" | 28 | #include "thermal_core.h" |
28 | 29 | ||
@@ -76,7 +77,7 @@ static unsigned long get_target_state(struct thermal_instance *instance, | |||
76 | next_target = instance->upper; | 77 | next_target = instance->upper; |
77 | break; | 78 | break; |
78 | case THERMAL_TREND_DROPPING: | 79 | case THERMAL_TREND_DROPPING: |
79 | if (cur_state == instance->lower) { | 80 | if (cur_state <= instance->lower) { |
80 | if (!throttle) | 81 | if (!throttle) |
81 | next_target = THERMAL_NO_TARGET; | 82 | next_target = THERMAL_NO_TARGET; |
82 | } else { | 83 | } else { |
@@ -129,8 +130,10 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) | |||
129 | 130 | ||
130 | trend = get_tz_trend(tz, trip); | 131 | trend = get_tz_trend(tz, trip); |
131 | 132 | ||
132 | if (tz->temperature >= trip_temp) | 133 | if (tz->temperature >= trip_temp) { |
133 | throttle = true; | 134 | throttle = true; |
135 | trace_thermal_zone_trip(tz, trip, trip_type); | ||
136 | } | ||
134 | 137 | ||
135 | dev_dbg(&tz->device, "Trip%d[type=%d,temp=%ld]:trend=%d,throttle=%d\n", | 138 | dev_dbg(&tz->device, "Trip%d[type=%d,temp=%ld]:trend=%d,throttle=%d\n", |
136 | trip, trip_type, trip_temp, trend, throttle); | 139 | trip, trip_type, trip_temp, trend, throttle); |
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 1e23f4f8d2c2..9bf10aa6069b 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c | |||
@@ -38,6 +38,9 @@ | |||
38 | #include <net/netlink.h> | 38 | #include <net/netlink.h> |
39 | #include <net/genetlink.h> | 39 | #include <net/genetlink.h> |
40 | 40 | ||
41 | #define CREATE_TRACE_POINTS | ||
42 | #include <trace/events/thermal.h> | ||
43 | |||
41 | #include "thermal_core.h" | 44 | #include "thermal_core.h" |
42 | #include "thermal_hwmon.h" | 45 | #include "thermal_hwmon.h" |
43 | 46 | ||
@@ -368,6 +371,8 @@ static void handle_critical_trips(struct thermal_zone_device *tz, | |||
368 | if (tz->temperature < trip_temp) | 371 | if (tz->temperature < trip_temp) |
369 | return; | 372 | return; |
370 | 373 | ||
374 | trace_thermal_zone_trip(tz, trip, trip_type); | ||
375 | |||
371 | if (tz->ops->notify) | 376 | if (tz->ops->notify) |
372 | tz->ops->notify(tz, trip, trip_type); | 377 | tz->ops->notify(tz, trip, trip_type); |
373 | 378 | ||
@@ -463,6 +468,7 @@ static void update_temperature(struct thermal_zone_device *tz) | |||
463 | tz->temperature = temp; | 468 | tz->temperature = temp; |
464 | mutex_unlock(&tz->lock); | 469 | mutex_unlock(&tz->lock); |
465 | 470 | ||
471 | trace_thermal_temperature(tz); | ||
466 | dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", | 472 | dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", |
467 | tz->last_temperature, tz->temperature); | 473 | tz->last_temperature, tz->temperature); |
468 | } | 474 | } |
@@ -1287,6 +1293,7 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev) | |||
1287 | mutex_unlock(&cdev->lock); | 1293 | mutex_unlock(&cdev->lock); |
1288 | cdev->ops->set_cur_state(cdev, target); | 1294 | cdev->ops->set_cur_state(cdev, target); |
1289 | cdev->updated = true; | 1295 | cdev->updated = true; |
1296 | trace_cdev_update(cdev, target); | ||
1290 | dev_dbg(&cdev->device, "set to state %lu\n", target); | 1297 | dev_dbg(&cdev->device, "set to state %lu\n", target); |
1291 | } | 1298 | } |
1292 | EXPORT_SYMBOL(thermal_cdev_update); | 1299 | EXPORT_SYMBOL(thermal_cdev_update); |
@@ -1790,6 +1797,10 @@ static int __init thermal_register_governors(void) | |||
1790 | if (result) | 1797 | if (result) |
1791 | return result; | 1798 | return result; |
1792 | 1799 | ||
1800 | result = thermal_gov_bang_bang_register(); | ||
1801 | if (result) | ||
1802 | return result; | ||
1803 | |||
1793 | return thermal_gov_user_space_register(); | 1804 | return thermal_gov_user_space_register(); |
1794 | } | 1805 | } |
1795 | 1806 | ||
@@ -1797,6 +1808,7 @@ static void thermal_unregister_governors(void) | |||
1797 | { | 1808 | { |
1798 | thermal_gov_step_wise_unregister(); | 1809 | thermal_gov_step_wise_unregister(); |
1799 | thermal_gov_fair_share_unregister(); | 1810 | thermal_gov_fair_share_unregister(); |
1811 | thermal_gov_bang_bang_unregister(); | ||
1800 | thermal_gov_user_space_unregister(); | 1812 | thermal_gov_user_space_unregister(); |
1801 | } | 1813 | } |
1802 | 1814 | ||
diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 3db339fb636f..d15d243de27a 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h | |||
@@ -69,6 +69,14 @@ static inline int thermal_gov_fair_share_register(void) { return 0; } | |||
69 | static inline void thermal_gov_fair_share_unregister(void) {} | 69 | static inline void thermal_gov_fair_share_unregister(void) {} |
70 | #endif /* CONFIG_THERMAL_GOV_FAIR_SHARE */ | 70 | #endif /* CONFIG_THERMAL_GOV_FAIR_SHARE */ |
71 | 71 | ||
72 | #ifdef CONFIG_THERMAL_GOV_BANG_BANG | ||
73 | int thermal_gov_bang_bang_register(void); | ||
74 | void thermal_gov_bang_bang_unregister(void); | ||
75 | #else | ||
76 | static inline int thermal_gov_bang_bang_register(void) { return 0; } | ||
77 | static inline void thermal_gov_bang_bang_unregister(void) {} | ||
78 | #endif /* CONFIG_THERMAL_GOV_BANG_BANG */ | ||
79 | |||
72 | #ifdef CONFIG_THERMAL_GOV_USER_SPACE | 80 | #ifdef CONFIG_THERMAL_GOV_USER_SPACE |
73 | int thermal_gov_user_space_register(void); | 81 | int thermal_gov_user_space_register(void); |
74 | void thermal_gov_user_space_unregister(void); | 82 | void thermal_gov_user_space_unregister(void); |
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index a673e5b6a2e0..60fa6278fbce 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c | |||
@@ -28,18 +28,6 @@ | |||
28 | 28 | ||
29 | #define UIO_MAX_DEVICES (1U << MINORBITS) | 29 | #define UIO_MAX_DEVICES (1U << MINORBITS) |
30 | 30 | ||
31 | struct uio_device { | ||
32 | struct module *owner; | ||
33 | struct device *dev; | ||
34 | int minor; | ||
35 | atomic_t event; | ||
36 | struct fasync_struct *async_queue; | ||
37 | wait_queue_head_t wait; | ||
38 | struct uio_info *info; | ||
39 | struct kobject *map_dir; | ||
40 | struct kobject *portio_dir; | ||
41 | }; | ||
42 | |||
43 | static int uio_major; | 31 | static int uio_major; |
44 | static struct cdev *uio_cdev; | 32 | static struct cdev *uio_cdev; |
45 | static DEFINE_IDR(uio_idr); | 33 | static DEFINE_IDR(uio_idr); |
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e3d5bf0a5021..d0107d424ee4 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -87,6 +87,15 @@ config DA9055_WATCHDOG | |||
87 | This driver can also be built as a module. If so, the module | 87 | This driver can also be built as a module. If so, the module |
88 | will be called da9055_wdt. | 88 | will be called da9055_wdt. |
89 | 89 | ||
90 | config DA9063_WATCHDOG | ||
91 | tristate "Dialog DA9063 Watchdog" | ||
92 | depends on MFD_DA9063 | ||
93 | select WATCHDOG_CORE | ||
94 | help | ||
95 | Support for the watchdog in the DA9063 PMIC. | ||
96 | |||
97 | This driver can be built as a module. The module name is da9063_wdt. | ||
98 | |||
90 | config GPIO_WATCHDOG | 99 | config GPIO_WATCHDOG |
91 | tristate "Watchdog device controlled through GPIO-line" | 100 | tristate "Watchdog device controlled through GPIO-line" |
92 | depends on OF_GPIO | 101 | depends on OF_GPIO |
@@ -123,6 +132,7 @@ config WM8350_WATCHDOG | |||
123 | 132 | ||
124 | config XILINX_WATCHDOG | 133 | config XILINX_WATCHDOG |
125 | tristate "Xilinx Watchdog timer" | 134 | tristate "Xilinx Watchdog timer" |
135 | depends on HAS_IOMEM | ||
126 | select WATCHDOG_CORE | 136 | select WATCHDOG_CORE |
127 | help | 137 | help |
128 | Watchdog driver for the xps_timebase_wdt ip core. | 138 | Watchdog driver for the xps_timebase_wdt ip core. |
@@ -157,6 +167,14 @@ config AT91SAM9X_WATCHDOG | |||
157 | Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will | 167 | Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will |
158 | reboot your system when the timeout is reached. | 168 | reboot your system when the timeout is reached. |
159 | 169 | ||
170 | config CADENCE_WATCHDOG | ||
171 | tristate "Cadence Watchdog Timer" | ||
172 | depends on ARM | ||
173 | select WATCHDOG_CORE | ||
174 | help | ||
175 | Say Y here if you want to include support for the watchdog | ||
176 | timer in the Xilinx Zynq. | ||
177 | |||
160 | config 21285_WATCHDOG | 178 | config 21285_WATCHDOG |
161 | tristate "DC21285 watchdog" | 179 | tristate "DC21285 watchdog" |
162 | depends on FOOTBRIDGE | 180 | depends on FOOTBRIDGE |
@@ -319,6 +337,17 @@ config ORION_WATCHDOG | |||
319 | To compile this driver as a module, choose M here: the | 337 | To compile this driver as a module, choose M here: the |
320 | module will be called orion_wdt. | 338 | module will be called orion_wdt. |
321 | 339 | ||
340 | config RN5T618_WATCHDOG | ||
341 | tristate "Ricoh RN5T618 watchdog" | ||
342 | depends on MFD_RN5T618 | ||
343 | select WATCHDOG_CORE | ||
344 | help | ||
345 | If you say yes here you get support for watchdog on the Ricoh | ||
346 | RN5T618 PMIC. | ||
347 | |||
348 | This driver can also be built as a module. If so, the module | ||
349 | will be called rn5t618_wdt. | ||
350 | |||
322 | config SUNXI_WATCHDOG | 351 | config SUNXI_WATCHDOG |
323 | tristate "Allwinner SoCs watchdog support" | 352 | tristate "Allwinner SoCs watchdog support" |
324 | depends on ARCH_SUNXI | 353 | depends on ARCH_SUNXI |
@@ -444,7 +473,7 @@ config SIRFSOC_WATCHDOG | |||
444 | 473 | ||
445 | config TEGRA_WATCHDOG | 474 | config TEGRA_WATCHDOG |
446 | tristate "Tegra watchdog" | 475 | tristate "Tegra watchdog" |
447 | depends on ARCH_TEGRA || COMPILE_TEST | 476 | depends on (ARCH_TEGRA || COMPILE_TEST) && HAS_IOMEM |
448 | select WATCHDOG_CORE | 477 | select WATCHDOG_CORE |
449 | help | 478 | help |
450 | Say Y here to include support for the watchdog timer | 479 | Say Y here to include support for the watchdog timer |
@@ -453,6 +482,29 @@ config TEGRA_WATCHDOG | |||
453 | To compile this driver as a module, choose M here: the | 482 | To compile this driver as a module, choose M here: the |
454 | module will be called tegra_wdt. | 483 | module will be called tegra_wdt. |
455 | 484 | ||
485 | config QCOM_WDT | ||
486 | tristate "QCOM watchdog" | ||
487 | depends on HAS_IOMEM | ||
488 | depends on ARCH_QCOM | ||
489 | select WATCHDOG_CORE | ||
490 | help | ||
491 | Say Y here to include Watchdog timer support for the watchdog found | ||
492 | on QCOM chipsets. Currently supported targets are the MSM8960, | ||
493 | APQ8064, and IPQ8064. | ||
494 | |||
495 | To compile this driver as a module, choose M here: the | ||
496 | module will be called qcom_wdt. | ||
497 | |||
498 | config MESON_WATCHDOG | ||
499 | tristate "Amlogic Meson SoCs watchdog support" | ||
500 | depends on ARCH_MESON | ||
501 | select WATCHDOG_CORE | ||
502 | help | ||
503 | Say Y here to include support for the watchdog timer | ||
504 | in Amlogic Meson SoCs. | ||
505 | To compile this driver as a module, choose M here: the | ||
506 | module will be called meson_wdt. | ||
507 | |||
456 | # AVR32 Architecture | 508 | # AVR32 Architecture |
457 | 509 | ||
458 | config AT32AP700X_WDT | 510 | config AT32AP700X_WDT |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index de1701470c14..c569ec8f8a76 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -32,6 +32,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o | |||
32 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o | 32 | obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o |
33 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o | 33 | obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o |
34 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o | 34 | obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o |
35 | obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o | ||
35 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o | 36 | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o |
36 | obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o | 37 | obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o |
37 | obj-$(CONFIG_21285_WATCHDOG) += wdt285.o | 38 | obj-$(CONFIG_21285_WATCHDOG) += wdt285.o |
@@ -47,6 +48,7 @@ obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o | |||
47 | obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o | 48 | obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o |
48 | obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o | 49 | obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o |
49 | obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o | 50 | obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o |
51 | obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o | ||
50 | obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o | 52 | obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o |
51 | obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o | 53 | obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o |
52 | obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o | 54 | obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o |
@@ -57,8 +59,10 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o | |||
57 | obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o | 59 | obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o |
58 | obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o | 60 | obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o |
59 | obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o | 61 | obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o |
62 | obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o | ||
60 | obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o | 63 | obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o |
61 | obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o | 64 | obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o |
65 | obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o | ||
62 | 66 | ||
63 | # AVR32 Architecture | 67 | # AVR32 Architecture |
64 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o | 68 | obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o |
@@ -173,6 +177,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o | |||
173 | # Architecture Independent | 177 | # Architecture Independent |
174 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o | 178 | obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o |
175 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o | 179 | obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o |
180 | obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o | ||
176 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o | 181 | obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o |
177 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o | 182 | obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o |
178 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o | 183 | obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o |
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 08a785398eac..e96b09b135c8 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c | |||
@@ -30,8 +30,6 @@ | |||
30 | * occur, and the final time the board will reset. | 30 | * occur, and the final time the board will reset. |
31 | */ | 31 | */ |
32 | 32 | ||
33 | u32 booke_wdt_enabled; | ||
34 | u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; | ||
35 | 33 | ||
36 | #ifdef CONFIG_PPC_FSL_BOOK3E | 34 | #ifdef CONFIG_PPC_FSL_BOOK3E |
37 | #define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15)) | 35 | #define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15)) |
@@ -41,27 +39,10 @@ u32 booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; | |||
41 | #define WDTP_MASK (TCR_WP_MASK) | 39 | #define WDTP_MASK (TCR_WP_MASK) |
42 | #endif | 40 | #endif |
43 | 41 | ||
44 | /* Checks wdt=x and wdt_period=xx command-line option */ | 42 | static bool booke_wdt_enabled; |
45 | notrace int __init early_parse_wdt(char *p) | 43 | module_param(booke_wdt_enabled, bool, 0); |
46 | { | 44 | static int booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; |
47 | if (p && strncmp(p, "0", 1) != 0) | 45 | module_param(booke_wdt_period, int, 0); |
48 | booke_wdt_enabled = 1; | ||
49 | |||
50 | return 0; | ||
51 | } | ||
52 | early_param("wdt", early_parse_wdt); | ||
53 | |||
54 | int __init early_parse_wdt_period(char *p) | ||
55 | { | ||
56 | unsigned long ret; | ||
57 | if (p) { | ||
58 | if (!kstrtol(p, 0, &ret)) | ||
59 | booke_wdt_period = ret; | ||
60 | } | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | early_param("wdt_period", early_parse_wdt_period); | ||
65 | 46 | ||
66 | #ifdef CONFIG_PPC_FSL_BOOK3E | 47 | #ifdef CONFIG_PPC_FSL_BOOK3E |
67 | 48 | ||
@@ -259,5 +240,6 @@ static int __init booke_wdt_init(void) | |||
259 | module_init(booke_wdt_init); | 240 | module_init(booke_wdt_init); |
260 | module_exit(booke_wdt_exit); | 241 | module_exit(booke_wdt_exit); |
261 | 242 | ||
243 | MODULE_ALIAS("booke_wdt"); | ||
262 | MODULE_DESCRIPTION("PowerPC Book-E watchdog driver"); | 244 | MODULE_DESCRIPTION("PowerPC Book-E watchdog driver"); |
263 | MODULE_LICENSE("GPL"); | 245 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c new file mode 100644 index 000000000000..5927c0a98a74 --- /dev/null +++ b/drivers/watchdog/cadence_wdt.c | |||
@@ -0,0 +1,516 @@ | |||
1 | /* | ||
2 | * Cadence WDT driver - Used by Xilinx Zynq | ||
3 | * | ||
4 | * Copyright (C) 2010 - 2014 Xilinx, Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/reboot.h> | ||
22 | #include <linux/watchdog.h> | ||
23 | |||
24 | #define CDNS_WDT_DEFAULT_TIMEOUT 10 | ||
25 | /* Supports 1 - 516 sec */ | ||
26 | #define CDNS_WDT_MIN_TIMEOUT 1 | ||
27 | #define CDNS_WDT_MAX_TIMEOUT 516 | ||
28 | |||
29 | /* Restart key */ | ||
30 | #define CDNS_WDT_RESTART_KEY 0x00001999 | ||
31 | |||
32 | /* Counter register access key */ | ||
33 | #define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000 | ||
34 | |||
35 | /* Counter value divisor */ | ||
36 | #define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000 | ||
37 | |||
38 | /* Clock prescaler value and selection */ | ||
39 | #define CDNS_WDT_PRESCALE_64 64 | ||
40 | #define CDNS_WDT_PRESCALE_512 512 | ||
41 | #define CDNS_WDT_PRESCALE_4096 4096 | ||
42 | #define CDNS_WDT_PRESCALE_SELECT_64 1 | ||
43 | #define CDNS_WDT_PRESCALE_SELECT_512 2 | ||
44 | #define CDNS_WDT_PRESCALE_SELECT_4096 3 | ||
45 | |||
46 | /* Input clock frequency */ | ||
47 | #define CDNS_WDT_CLK_10MHZ 10000000 | ||
48 | #define CDNS_WDT_CLK_75MHZ 75000000 | ||
49 | |||
50 | /* Counter maximum value */ | ||
51 | #define CDNS_WDT_COUNTER_MAX 0xFFF | ||
52 | |||
53 | static int wdt_timeout = CDNS_WDT_DEFAULT_TIMEOUT; | ||
54 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
55 | |||
56 | module_param(wdt_timeout, int, 0); | ||
57 | MODULE_PARM_DESC(wdt_timeout, | ||
58 | "Watchdog time in seconds. (default=" | ||
59 | __MODULE_STRING(CDNS_WDT_DEFAULT_TIMEOUT) ")"); | ||
60 | |||
61 | module_param(nowayout, int, 0); | ||
62 | MODULE_PARM_DESC(nowayout, | ||
63 | "Watchdog cannot be stopped once started (default=" | ||
64 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
65 | |||
66 | /** | ||
67 | * struct cdns_wdt - Watchdog device structure | ||
68 | * @regs: baseaddress of device | ||
69 | * @rst: reset flag | ||
70 | * @clk: struct clk * of a clock source | ||
71 | * @prescaler: for saving prescaler value | ||
72 | * @ctrl_clksel: counter clock prescaler selection | ||
73 | * @io_lock: spinlock for IO register access | ||
74 | * @cdns_wdt_device: watchdog device structure | ||
75 | * @cdns_wdt_notifier: notifier structure | ||
76 | * | ||
77 | * Structure containing parameters specific to cadence watchdog. | ||
78 | */ | ||
79 | struct cdns_wdt { | ||
80 | void __iomem *regs; | ||
81 | bool rst; | ||
82 | struct clk *clk; | ||
83 | u32 prescaler; | ||
84 | u32 ctrl_clksel; | ||
85 | spinlock_t io_lock; | ||
86 | struct watchdog_device cdns_wdt_device; | ||
87 | struct notifier_block cdns_wdt_notifier; | ||
88 | }; | ||
89 | |||
90 | /* Write access to Registers */ | ||
91 | static inline void cdns_wdt_writereg(struct cdns_wdt *wdt, u32 offset, u32 val) | ||
92 | { | ||
93 | writel_relaxed(val, wdt->regs + offset); | ||
94 | } | ||
95 | |||
96 | /*************************Register Map**************************************/ | ||
97 | |||
98 | /* Register Offsets for the WDT */ | ||
99 | #define CDNS_WDT_ZMR_OFFSET 0x0 /* Zero Mode Register */ | ||
100 | #define CDNS_WDT_CCR_OFFSET 0x4 /* Counter Control Register */ | ||
101 | #define CDNS_WDT_RESTART_OFFSET 0x8 /* Restart Register */ | ||
102 | #define CDNS_WDT_SR_OFFSET 0xC /* Status Register */ | ||
103 | |||
104 | /* | ||
105 | * Zero Mode Register - This register controls how the time out is indicated | ||
106 | * and also contains the access code to allow writes to the register (0xABC). | ||
107 | */ | ||
108 | #define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ | ||
109 | #define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ | ||
110 | #define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ | ||
111 | #define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ | ||
112 | #define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ | ||
113 | /* | ||
114 | * Counter Control register - This register controls how fast the timer runs | ||
115 | * and the reset value and also contains the access code to allow writes to | ||
116 | * the register. | ||
117 | */ | ||
118 | #define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ | ||
119 | |||
120 | /** | ||
121 | * cdns_wdt_stop - Stop the watchdog. | ||
122 | * | ||
123 | * @wdd: watchdog device | ||
124 | * | ||
125 | * Read the contents of the ZMR register, clear the WDEN bit | ||
126 | * in the register and set the access key for successful write. | ||
127 | * | ||
128 | * Return: always 0 | ||
129 | */ | ||
130 | static int cdns_wdt_stop(struct watchdog_device *wdd) | ||
131 | { | ||
132 | struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); | ||
133 | |||
134 | spin_lock(&wdt->io_lock); | ||
135 | cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, | ||
136 | CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK)); | ||
137 | spin_unlock(&wdt->io_lock); | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * cdns_wdt_reload - Reload the watchdog timer (i.e. pat the watchdog). | ||
144 | * | ||
145 | * @wdd: watchdog device | ||
146 | * | ||
147 | * Write the restart key value (0x00001999) to the restart register. | ||
148 | * | ||
149 | * Return: always 0 | ||
150 | */ | ||
151 | static int cdns_wdt_reload(struct watchdog_device *wdd) | ||
152 | { | ||
153 | struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); | ||
154 | |||
155 | spin_lock(&wdt->io_lock); | ||
156 | cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, | ||
157 | CDNS_WDT_RESTART_KEY); | ||
158 | spin_unlock(&wdt->io_lock); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * cdns_wdt_start - Enable and start the watchdog. | ||
165 | * | ||
166 | * @wdd: watchdog device | ||
167 | * | ||
168 | * The counter value is calculated according to the formula: | ||
169 | * calculated count = (timeout * clock) / prescaler + 1. | ||
170 | * The calculated count is divided by 0x1000 to obtain the field value | ||
171 | * to write to counter control register. | ||
172 | * Clears the contents of prescaler and counter reset value. Sets the | ||
173 | * prescaler to 4096 and the calculated count and access key | ||
174 | * to write to CCR Register. | ||
175 | * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) | ||
176 | * or Interrupt signal(IRQEN) with a specified cycles and the access | ||
177 | * key to write to ZMR Register. | ||
178 | * | ||
179 | * Return: always 0 | ||
180 | */ | ||
181 | static int cdns_wdt_start(struct watchdog_device *wdd) | ||
182 | { | ||
183 | struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); | ||
184 | unsigned int data = 0; | ||
185 | unsigned short count; | ||
186 | unsigned long clock_f = clk_get_rate(wdt->clk); | ||
187 | |||
188 | /* | ||
189 | * Counter value divisor to obtain the value of | ||
190 | * counter reset to be written to control register. | ||
191 | */ | ||
192 | count = (wdd->timeout * (clock_f / wdt->prescaler)) / | ||
193 | CDNS_WDT_COUNTER_VALUE_DIVISOR + 1; | ||
194 | |||
195 | if (count > CDNS_WDT_COUNTER_MAX) | ||
196 | count = CDNS_WDT_COUNTER_MAX; | ||
197 | |||
198 | spin_lock(&wdt->io_lock); | ||
199 | cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, | ||
200 | CDNS_WDT_ZMR_ZKEY_VAL); | ||
201 | |||
202 | count = (count << 2) & CDNS_WDT_CCR_CRV_MASK; | ||
203 | |||
204 | /* Write counter access key first to be able write to register */ | ||
205 | data = count | CDNS_WDT_REGISTER_ACCESS_KEY | wdt->ctrl_clksel; | ||
206 | cdns_wdt_writereg(wdt, CDNS_WDT_CCR_OFFSET, data); | ||
207 | data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 | | ||
208 | CDNS_WDT_ZMR_ZKEY_VAL; | ||
209 | |||
210 | /* Reset on timeout if specified in device tree. */ | ||
211 | if (wdt->rst) { | ||
212 | data |= CDNS_WDT_ZMR_RSTEN_MASK; | ||
213 | data &= ~CDNS_WDT_ZMR_IRQEN_MASK; | ||
214 | } else { | ||
215 | data &= ~CDNS_WDT_ZMR_RSTEN_MASK; | ||
216 | data |= CDNS_WDT_ZMR_IRQEN_MASK; | ||
217 | } | ||
218 | cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, data); | ||
219 | cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, | ||
220 | CDNS_WDT_RESTART_KEY); | ||
221 | spin_unlock(&wdt->io_lock); | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * cdns_wdt_settimeout - Set a new timeout value for the watchdog device. | ||
228 | * | ||
229 | * @wdd: watchdog device | ||
230 | * @new_time: new timeout value that needs to be set | ||
231 | * Return: 0 on success | ||
232 | * | ||
233 | * Update the watchdog_device timeout with new value which is used when | ||
234 | * cdns_wdt_start is called. | ||
235 | */ | ||
236 | static int cdns_wdt_settimeout(struct watchdog_device *wdd, | ||
237 | unsigned int new_time) | ||
238 | { | ||
239 | wdd->timeout = new_time; | ||
240 | |||
241 | return cdns_wdt_start(wdd); | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * cdns_wdt_irq_handler - Notifies of watchdog timeout. | ||
246 | * | ||
247 | * @irq: interrupt number | ||
248 | * @dev_id: pointer to a platform device structure | ||
249 | * Return: IRQ_HANDLED | ||
250 | * | ||
251 | * The handler is invoked when the watchdog times out and a | ||
252 | * reset on timeout has not been enabled. | ||
253 | */ | ||
254 | static irqreturn_t cdns_wdt_irq_handler(int irq, void *dev_id) | ||
255 | { | ||
256 | struct platform_device *pdev = dev_id; | ||
257 | |||
258 | dev_info(&pdev->dev, | ||
259 | "Watchdog timed out. Internal reset not enabled\n"); | ||
260 | |||
261 | return IRQ_HANDLED; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * Info structure used to indicate the features supported by the device | ||
266 | * to the upper layers. This is defined in watchdog.h header file. | ||
267 | */ | ||
268 | static struct watchdog_info cdns_wdt_info = { | ||
269 | .identity = "cdns_wdt watchdog", | ||
270 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | | ||
271 | WDIOF_MAGICCLOSE, | ||
272 | }; | ||
273 | |||
274 | /* Watchdog Core Ops */ | ||
275 | static struct watchdog_ops cdns_wdt_ops = { | ||
276 | .owner = THIS_MODULE, | ||
277 | .start = cdns_wdt_start, | ||
278 | .stop = cdns_wdt_stop, | ||
279 | .ping = cdns_wdt_reload, | ||
280 | .set_timeout = cdns_wdt_settimeout, | ||
281 | }; | ||
282 | |||
283 | /** | ||
284 | * cdns_wdt_notify_sys - Notifier for reboot or shutdown. | ||
285 | * | ||
286 | * @this: handle to notifier block | ||
287 | * @code: turn off indicator | ||
288 | * @unused: unused | ||
289 | * Return: NOTIFY_DONE | ||
290 | * | ||
291 | * This notifier is invoked whenever the system reboot or shutdown occur | ||
292 | * because we need to disable the WDT before system goes down as WDT might | ||
293 | * reset on the next boot. | ||
294 | */ | ||
295 | static int cdns_wdt_notify_sys(struct notifier_block *this, unsigned long code, | ||
296 | void *unused) | ||
297 | { | ||
298 | struct cdns_wdt *wdt = container_of(this, struct cdns_wdt, | ||
299 | cdns_wdt_notifier); | ||
300 | if (code == SYS_DOWN || code == SYS_HALT) | ||
301 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
302 | |||
303 | return NOTIFY_DONE; | ||
304 | } | ||
305 | |||
306 | /************************Platform Operations*****************************/ | ||
307 | /** | ||
308 | * cdns_wdt_probe - Probe call for the device. | ||
309 | * | ||
310 | * @pdev: handle to the platform device structure. | ||
311 | * Return: 0 on success, negative error otherwise. | ||
312 | * | ||
313 | * It does all the memory allocation and registration for the device. | ||
314 | */ | ||
315 | static int cdns_wdt_probe(struct platform_device *pdev) | ||
316 | { | ||
317 | struct resource *res; | ||
318 | int ret, irq; | ||
319 | unsigned long clock_f; | ||
320 | struct cdns_wdt *wdt; | ||
321 | struct watchdog_device *cdns_wdt_device; | ||
322 | |||
323 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
324 | if (!wdt) | ||
325 | return -ENOMEM; | ||
326 | |||
327 | cdns_wdt_device = &wdt->cdns_wdt_device; | ||
328 | cdns_wdt_device->info = &cdns_wdt_info; | ||
329 | cdns_wdt_device->ops = &cdns_wdt_ops; | ||
330 | cdns_wdt_device->timeout = CDNS_WDT_DEFAULT_TIMEOUT; | ||
331 | cdns_wdt_device->min_timeout = CDNS_WDT_MIN_TIMEOUT; | ||
332 | cdns_wdt_device->max_timeout = CDNS_WDT_MAX_TIMEOUT; | ||
333 | |||
334 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
335 | wdt->regs = devm_ioremap_resource(&pdev->dev, res); | ||
336 | if (IS_ERR(wdt->regs)) | ||
337 | return PTR_ERR(wdt->regs); | ||
338 | |||
339 | /* Register the interrupt */ | ||
340 | wdt->rst = of_property_read_bool(pdev->dev.of_node, "reset-on-timeout"); | ||
341 | irq = platform_get_irq(pdev, 0); | ||
342 | if (!wdt->rst && irq >= 0) { | ||
343 | ret = devm_request_irq(&pdev->dev, irq, cdns_wdt_irq_handler, 0, | ||
344 | pdev->name, pdev); | ||
345 | if (ret) { | ||
346 | dev_err(&pdev->dev, | ||
347 | "cannot register interrupt handler err=%d\n", | ||
348 | ret); | ||
349 | return ret; | ||
350 | } | ||
351 | } | ||
352 | |||
353 | /* Initialize the members of cdns_wdt structure */ | ||
354 | cdns_wdt_device->parent = &pdev->dev; | ||
355 | |||
356 | ret = watchdog_init_timeout(cdns_wdt_device, wdt_timeout, &pdev->dev); | ||
357 | if (ret) { | ||
358 | dev_err(&pdev->dev, "unable to set timeout value\n"); | ||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | watchdog_set_nowayout(cdns_wdt_device, nowayout); | ||
363 | watchdog_set_drvdata(cdns_wdt_device, wdt); | ||
364 | |||
365 | wdt->clk = devm_clk_get(&pdev->dev, NULL); | ||
366 | if (IS_ERR(wdt->clk)) { | ||
367 | dev_err(&pdev->dev, "input clock not found\n"); | ||
368 | ret = PTR_ERR(wdt->clk); | ||
369 | return ret; | ||
370 | } | ||
371 | |||
372 | ret = clk_prepare_enable(wdt->clk); | ||
373 | if (ret) { | ||
374 | dev_err(&pdev->dev, "unable to enable clock\n"); | ||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | clock_f = clk_get_rate(wdt->clk); | ||
379 | if (clock_f <= CDNS_WDT_CLK_75MHZ) { | ||
380 | wdt->prescaler = CDNS_WDT_PRESCALE_512; | ||
381 | wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512; | ||
382 | } else { | ||
383 | wdt->prescaler = CDNS_WDT_PRESCALE_4096; | ||
384 | wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096; | ||
385 | } | ||
386 | |||
387 | spin_lock_init(&wdt->io_lock); | ||
388 | |||
389 | wdt->cdns_wdt_notifier.notifier_call = &cdns_wdt_notify_sys; | ||
390 | ret = register_reboot_notifier(&wdt->cdns_wdt_notifier); | ||
391 | if (ret != 0) { | ||
392 | dev_err(&pdev->dev, "cannot register reboot notifier err=%d)\n", | ||
393 | ret); | ||
394 | goto err_clk_disable; | ||
395 | } | ||
396 | |||
397 | ret = watchdog_register_device(cdns_wdt_device); | ||
398 | if (ret) { | ||
399 | dev_err(&pdev->dev, "Failed to register wdt device\n"); | ||
400 | goto err_clk_disable; | ||
401 | } | ||
402 | platform_set_drvdata(pdev, wdt); | ||
403 | |||
404 | dev_dbg(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds%s\n", | ||
405 | wdt->regs, cdns_wdt_device->timeout, | ||
406 | nowayout ? ", nowayout" : ""); | ||
407 | |||
408 | return 0; | ||
409 | |||
410 | err_clk_disable: | ||
411 | clk_disable_unprepare(wdt->clk); | ||
412 | |||
413 | return ret; | ||
414 | } | ||
415 | |||
416 | /** | ||
417 | * cdns_wdt_remove - Probe call for the device. | ||
418 | * | ||
419 | * @pdev: handle to the platform device structure. | ||
420 | * Return: 0 on success, otherwise negative error. | ||
421 | * | ||
422 | * Unregister the device after releasing the resources. | ||
423 | */ | ||
424 | static int cdns_wdt_remove(struct platform_device *pdev) | ||
425 | { | ||
426 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
427 | |||
428 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
429 | watchdog_unregister_device(&wdt->cdns_wdt_device); | ||
430 | unregister_reboot_notifier(&wdt->cdns_wdt_notifier); | ||
431 | clk_disable_unprepare(wdt->clk); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /** | ||
437 | * cdns_wdt_shutdown - Stop the device. | ||
438 | * | ||
439 | * @pdev: handle to the platform structure. | ||
440 | * | ||
441 | */ | ||
442 | static void cdns_wdt_shutdown(struct platform_device *pdev) | ||
443 | { | ||
444 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
445 | |||
446 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
447 | clk_disable_unprepare(wdt->clk); | ||
448 | } | ||
449 | |||
450 | /** | ||
451 | * cdns_wdt_suspend - Stop the device. | ||
452 | * | ||
453 | * @dev: handle to the device structure. | ||
454 | * Return: 0 always. | ||
455 | */ | ||
456 | static int __maybe_unused cdns_wdt_suspend(struct device *dev) | ||
457 | { | ||
458 | struct platform_device *pdev = container_of(dev, | ||
459 | struct platform_device, dev); | ||
460 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
461 | |||
462 | cdns_wdt_stop(&wdt->cdns_wdt_device); | ||
463 | clk_disable_unprepare(wdt->clk); | ||
464 | |||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | /** | ||
469 | * cdns_wdt_resume - Resume the device. | ||
470 | * | ||
471 | * @dev: handle to the device structure. | ||
472 | * Return: 0 on success, errno otherwise. | ||
473 | */ | ||
474 | static int __maybe_unused cdns_wdt_resume(struct device *dev) | ||
475 | { | ||
476 | int ret; | ||
477 | struct platform_device *pdev = container_of(dev, | ||
478 | struct platform_device, dev); | ||
479 | struct cdns_wdt *wdt = platform_get_drvdata(pdev); | ||
480 | |||
481 | ret = clk_prepare_enable(wdt->clk); | ||
482 | if (ret) { | ||
483 | dev_err(dev, "unable to enable clock\n"); | ||
484 | return ret; | ||
485 | } | ||
486 | cdns_wdt_start(&wdt->cdns_wdt_device); | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume); | ||
492 | |||
493 | static struct of_device_id cdns_wdt_of_match[] = { | ||
494 | { .compatible = "cdns,wdt-r1p2", }, | ||
495 | { /* end of table */ } | ||
496 | }; | ||
497 | MODULE_DEVICE_TABLE(of, cdns_wdt_of_match); | ||
498 | |||
499 | /* Driver Structure */ | ||
500 | static struct platform_driver cdns_wdt_driver = { | ||
501 | .probe = cdns_wdt_probe, | ||
502 | .remove = cdns_wdt_remove, | ||
503 | .shutdown = cdns_wdt_shutdown, | ||
504 | .driver = { | ||
505 | .name = "cdns-wdt", | ||
506 | .owner = THIS_MODULE, | ||
507 | .of_match_table = cdns_wdt_of_match, | ||
508 | .pm = &cdns_wdt_pm_ops, | ||
509 | }, | ||
510 | }; | ||
511 | |||
512 | module_platform_driver(cdns_wdt_driver); | ||
513 | |||
514 | MODULE_AUTHOR("Xilinx, Inc."); | ||
515 | MODULE_DESCRIPTION("Watchdog driver for Cadence WDT"); | ||
516 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c new file mode 100644 index 000000000000..2cd6b2c2dd2a --- /dev/null +++ b/drivers/watchdog/da9063_wdt.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * Watchdog driver for DA9063 PMICs. | ||
3 | * | ||
4 | * Copyright(c) 2012 Dialog Semiconductor Ltd. | ||
5 | * | ||
6 | * Author: Mariusz Wojtasik <mariusz.wojtasik@diasemi.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/watchdog.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/uaccess.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/mfd/da9063/registers.h> | ||
22 | #include <linux/mfd/da9063/core.h> | ||
23 | #include <linux/regmap.h> | ||
24 | |||
25 | /* | ||
26 | * Watchdog selector to timeout in seconds. | ||
27 | * 0: WDT disabled; | ||
28 | * others: timeout = 2048 ms * 2^(TWDSCALE-1). | ||
29 | */ | ||
30 | static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; | ||
31 | #define DA9063_TWDSCALE_DISABLE 0 | ||
32 | #define DA9063_TWDSCALE_MIN 1 | ||
33 | #define DA9063_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1) | ||
34 | #define DA9063_WDT_MIN_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MIN] | ||
35 | #define DA9063_WDT_MAX_TIMEOUT wdt_timeout[DA9063_TWDSCALE_MAX] | ||
36 | #define DA9063_WDG_TIMEOUT wdt_timeout[3] | ||
37 | |||
38 | struct da9063_watchdog { | ||
39 | struct da9063 *da9063; | ||
40 | struct watchdog_device wdtdev; | ||
41 | }; | ||
42 | |||
43 | static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs) | ||
44 | { | ||
45 | unsigned int i; | ||
46 | |||
47 | for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) { | ||
48 | if (wdt_timeout[i] >= secs) | ||
49 | return i; | ||
50 | } | ||
51 | |||
52 | return DA9063_TWDSCALE_MAX; | ||
53 | } | ||
54 | |||
55 | static int _da9063_wdt_set_timeout(struct da9063 *da9063, unsigned int regval) | ||
56 | { | ||
57 | return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D, | ||
58 | DA9063_TWDSCALE_MASK, regval); | ||
59 | } | ||
60 | |||
61 | static int da9063_wdt_start(struct watchdog_device *wdd) | ||
62 | { | ||
63 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
64 | unsigned int selector; | ||
65 | int ret; | ||
66 | |||
67 | selector = da9063_wdt_timeout_to_sel(wdt->wdtdev.timeout); | ||
68 | ret = _da9063_wdt_set_timeout(wdt->da9063, selector); | ||
69 | if (ret) | ||
70 | dev_err(wdt->da9063->dev, "Watchdog failed to start (err = %d)\n", | ||
71 | ret); | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static int da9063_wdt_stop(struct watchdog_device *wdd) | ||
77 | { | ||
78 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
79 | int ret; | ||
80 | |||
81 | ret = regmap_update_bits(wdt->da9063->regmap, DA9063_REG_CONTROL_D, | ||
82 | DA9063_TWDSCALE_MASK, DA9063_TWDSCALE_DISABLE); | ||
83 | if (ret) | ||
84 | dev_alert(wdt->da9063->dev, "Watchdog failed to stop (err = %d)\n", | ||
85 | ret); | ||
86 | |||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | static int da9063_wdt_ping(struct watchdog_device *wdd) | ||
91 | { | ||
92 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
93 | int ret; | ||
94 | |||
95 | ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F, | ||
96 | DA9063_WATCHDOG); | ||
97 | if (ret) | ||
98 | dev_alert(wdt->da9063->dev, "Failed to ping the watchdog (err = %d)\n", | ||
99 | ret); | ||
100 | |||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | static int da9063_wdt_set_timeout(struct watchdog_device *wdd, | ||
105 | unsigned int timeout) | ||
106 | { | ||
107 | struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd); | ||
108 | unsigned int selector; | ||
109 | int ret; | ||
110 | |||
111 | selector = da9063_wdt_timeout_to_sel(timeout); | ||
112 | ret = _da9063_wdt_set_timeout(wdt->da9063, selector); | ||
113 | if (ret) | ||
114 | dev_err(wdt->da9063->dev, "Failed to set watchdog timeout (err = %d)\n", | ||
115 | ret); | ||
116 | else | ||
117 | wdd->timeout = wdt_timeout[selector]; | ||
118 | |||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | static const struct watchdog_info da9063_watchdog_info = { | ||
123 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | ||
124 | .identity = "DA9063 Watchdog", | ||
125 | }; | ||
126 | |||
127 | static const struct watchdog_ops da9063_watchdog_ops = { | ||
128 | .owner = THIS_MODULE, | ||
129 | .start = da9063_wdt_start, | ||
130 | .stop = da9063_wdt_stop, | ||
131 | .ping = da9063_wdt_ping, | ||
132 | .set_timeout = da9063_wdt_set_timeout, | ||
133 | }; | ||
134 | |||
135 | static int da9063_wdt_probe(struct platform_device *pdev) | ||
136 | { | ||
137 | int ret; | ||
138 | struct da9063 *da9063; | ||
139 | struct da9063_watchdog *wdt; | ||
140 | |||
141 | if (!pdev->dev.parent) | ||
142 | return -EINVAL; | ||
143 | |||
144 | da9063 = dev_get_drvdata(pdev->dev.parent); | ||
145 | if (!da9063) | ||
146 | return -EINVAL; | ||
147 | |||
148 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
149 | if (!wdt) | ||
150 | return -ENOMEM; | ||
151 | |||
152 | wdt->da9063 = da9063; | ||
153 | |||
154 | wdt->wdtdev.info = &da9063_watchdog_info; | ||
155 | wdt->wdtdev.ops = &da9063_watchdog_ops; | ||
156 | wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT; | ||
157 | wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT; | ||
158 | wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT; | ||
159 | |||
160 | wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; | ||
161 | |||
162 | watchdog_set_drvdata(&wdt->wdtdev, wdt); | ||
163 | dev_set_drvdata(&pdev->dev, wdt); | ||
164 | |||
165 | ret = watchdog_register_device(&wdt->wdtdev); | ||
166 | |||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | static int da9063_wdt_remove(struct platform_device *pdev) | ||
171 | { | ||
172 | struct da9063_watchdog *wdt = dev_get_drvdata(&pdev->dev); | ||
173 | |||
174 | watchdog_unregister_device(&wdt->wdtdev); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static struct platform_driver da9063_wdt_driver = { | ||
180 | .probe = da9063_wdt_probe, | ||
181 | .remove = da9063_wdt_remove, | ||
182 | .driver = { | ||
183 | .name = DA9063_DRVNAME_WATCHDOG, | ||
184 | }, | ||
185 | }; | ||
186 | module_platform_driver(da9063_wdt_driver); | ||
187 | |||
188 | MODULE_AUTHOR("Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>"); | ||
189 | MODULE_DESCRIPTION("Watchdog driver for Dialog DA9063"); | ||
190 | MODULE_LICENSE("GPL"); | ||
191 | MODULE_ALIAS("platform:" DA9063_DRVNAME_WATCHDOG); | ||
diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 9f210299de24..9e577a64ec9e 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c | |||
@@ -21,6 +21,7 @@ | |||
21 | 21 | ||
22 | #include <linux/bitops.h> | 22 | #include <linux/bitops.h> |
23 | #include <linux/clk.h> | 23 | #include <linux/clk.h> |
24 | #include <linux/delay.h> | ||
24 | #include <linux/device.h> | 25 | #include <linux/device.h> |
25 | #include <linux/err.h> | 26 | #include <linux/err.h> |
26 | #include <linux/fs.h> | 27 | #include <linux/fs.h> |
@@ -29,9 +30,11 @@ | |||
29 | #include <linux/miscdevice.h> | 30 | #include <linux/miscdevice.h> |
30 | #include <linux/module.h> | 31 | #include <linux/module.h> |
31 | #include <linux/moduleparam.h> | 32 | #include <linux/moduleparam.h> |
33 | #include <linux/notifier.h> | ||
32 | #include <linux/of.h> | 34 | #include <linux/of.h> |
33 | #include <linux/pm.h> | 35 | #include <linux/pm.h> |
34 | #include <linux/platform_device.h> | 36 | #include <linux/platform_device.h> |
37 | #include <linux/reboot.h> | ||
35 | #include <linux/spinlock.h> | 38 | #include <linux/spinlock.h> |
36 | #include <linux/timer.h> | 39 | #include <linux/timer.h> |
37 | #include <linux/uaccess.h> | 40 | #include <linux/uaccess.h> |
@@ -40,6 +43,7 @@ | |||
40 | #define WDOG_CONTROL_REG_OFFSET 0x00 | 43 | #define WDOG_CONTROL_REG_OFFSET 0x00 |
41 | #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 | 44 | #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 |
42 | #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 | 45 | #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 |
46 | #define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT 4 | ||
43 | #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 | 47 | #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 |
44 | #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c | 48 | #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c |
45 | #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 | 49 | #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 |
@@ -62,6 +66,7 @@ static struct { | |||
62 | unsigned long next_heartbeat; | 66 | unsigned long next_heartbeat; |
63 | struct timer_list timer; | 67 | struct timer_list timer; |
64 | int expect_close; | 68 | int expect_close; |
69 | struct notifier_block restart_handler; | ||
65 | } dw_wdt; | 70 | } dw_wdt; |
66 | 71 | ||
67 | static inline int dw_wdt_is_enabled(void) | 72 | static inline int dw_wdt_is_enabled(void) |
@@ -106,7 +111,8 @@ static int dw_wdt_set_top(unsigned top_s) | |||
106 | } | 111 | } |
107 | 112 | ||
108 | /* Set the new value in the watchdog. */ | 113 | /* Set the new value in the watchdog. */ |
109 | writel(top_val, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | 114 | writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, |
115 | dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | ||
110 | 116 | ||
111 | dw_wdt_set_next_heartbeat(); | 117 | dw_wdt_set_next_heartbeat(); |
112 | 118 | ||
@@ -119,6 +125,26 @@ static void dw_wdt_keepalive(void) | |||
119 | WDOG_COUNTER_RESTART_REG_OFFSET); | 125 | WDOG_COUNTER_RESTART_REG_OFFSET); |
120 | } | 126 | } |
121 | 127 | ||
128 | static int dw_wdt_restart_handle(struct notifier_block *this, | ||
129 | unsigned long mode, void *cmd) | ||
130 | { | ||
131 | u32 val; | ||
132 | |||
133 | writel(0, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); | ||
134 | val = readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); | ||
135 | if (val & WDOG_CONTROL_REG_WDT_EN_MASK) | ||
136 | writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + | ||
137 | WDOG_COUNTER_RESTART_REG_OFFSET); | ||
138 | else | ||
139 | writel(WDOG_CONTROL_REG_WDT_EN_MASK, | ||
140 | dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); | ||
141 | |||
142 | /* wait for reset to assert... */ | ||
143 | mdelay(500); | ||
144 | |||
145 | return NOTIFY_DONE; | ||
146 | } | ||
147 | |||
122 | static void dw_wdt_ping(unsigned long data) | 148 | static void dw_wdt_ping(unsigned long data) |
123 | { | 149 | { |
124 | if (time_before(jiffies, dw_wdt.next_heartbeat) || | 150 | if (time_before(jiffies, dw_wdt.next_heartbeat) || |
@@ -314,6 +340,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) | |||
314 | if (ret) | 340 | if (ret) |
315 | goto out_disable_clk; | 341 | goto out_disable_clk; |
316 | 342 | ||
343 | dw_wdt.restart_handler.notifier_call = dw_wdt_restart_handle; | ||
344 | dw_wdt.restart_handler.priority = 128; | ||
345 | ret = register_restart_handler(&dw_wdt.restart_handler); | ||
346 | if (ret) | ||
347 | pr_warn("cannot register restart handler\n"); | ||
348 | |||
317 | dw_wdt_set_next_heartbeat(); | 349 | dw_wdt_set_next_heartbeat(); |
318 | setup_timer(&dw_wdt.timer, dw_wdt_ping, 0); | 350 | setup_timer(&dw_wdt.timer, dw_wdt_ping, 0); |
319 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); | 351 | mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); |
@@ -328,6 +360,8 @@ out_disable_clk: | |||
328 | 360 | ||
329 | static int dw_wdt_drv_remove(struct platform_device *pdev) | 361 | static int dw_wdt_drv_remove(struct platform_device *pdev) |
330 | { | 362 | { |
363 | unregister_restart_handler(&dw_wdt.restart_handler); | ||
364 | |||
331 | misc_deregister(&dw_wdt_miscdev); | 365 | misc_deregister(&dw_wdt_miscdev); |
332 | 366 | ||
333 | clk_disable_unprepare(dw_wdt.clk); | 367 | clk_disable_unprepare(dw_wdt.clk); |
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 68c3d379ffa8..7e12f88bb4a6 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c | |||
@@ -22,14 +22,17 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
25 | #include <linux/delay.h> | ||
25 | #include <linux/init.h> | 26 | #include <linux/init.h> |
26 | #include <linux/io.h> | 27 | #include <linux/io.h> |
27 | #include <linux/jiffies.h> | 28 | #include <linux/jiffies.h> |
28 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
29 | #include <linux/module.h> | 30 | #include <linux/module.h> |
30 | #include <linux/moduleparam.h> | 31 | #include <linux/moduleparam.h> |
32 | #include <linux/notifier.h> | ||
31 | #include <linux/of_address.h> | 33 | #include <linux/of_address.h> |
32 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
35 | #include <linux/reboot.h> | ||
33 | #include <linux/regmap.h> | 36 | #include <linux/regmap.h> |
34 | #include <linux/timer.h> | 37 | #include <linux/timer.h> |
35 | #include <linux/watchdog.h> | 38 | #include <linux/watchdog.h> |
@@ -59,6 +62,7 @@ struct imx2_wdt_device { | |||
59 | struct regmap *regmap; | 62 | struct regmap *regmap; |
60 | struct timer_list timer; /* Pings the watchdog when closed */ | 63 | struct timer_list timer; /* Pings the watchdog when closed */ |
61 | struct watchdog_device wdog; | 64 | struct watchdog_device wdog; |
65 | struct notifier_block restart_handler; | ||
62 | }; | 66 | }; |
63 | 67 | ||
64 | static bool nowayout = WATCHDOG_NOWAYOUT; | 68 | static bool nowayout = WATCHDOG_NOWAYOUT; |
@@ -77,6 +81,31 @@ static const struct watchdog_info imx2_wdt_info = { | |||
77 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, | 81 | .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, |
78 | }; | 82 | }; |
79 | 83 | ||
84 | static int imx2_restart_handler(struct notifier_block *this, unsigned long mode, | ||
85 | void *cmd) | ||
86 | { | ||
87 | unsigned int wcr_enable = IMX2_WDT_WCR_WDE; | ||
88 | struct imx2_wdt_device *wdev = container_of(this, | ||
89 | struct imx2_wdt_device, | ||
90 | restart_handler); | ||
91 | /* Assert SRS signal */ | ||
92 | regmap_write(wdev->regmap, 0, wcr_enable); | ||
93 | /* | ||
94 | * Due to imx6q errata ERR004346 (WDOG: WDOG SRS bit requires to be | ||
95 | * written twice), we add another two writes to ensure there must be at | ||
96 | * least two writes happen in the same one 32kHz clock period. We save | ||
97 | * the target check here, since the writes shouldn't be a huge burden | ||
98 | * for other platforms. | ||
99 | */ | ||
100 | regmap_write(wdev->regmap, 0, wcr_enable); | ||
101 | regmap_write(wdev->regmap, 0, wcr_enable); | ||
102 | |||
103 | /* wait for reset to assert... */ | ||
104 | mdelay(500); | ||
105 | |||
106 | return NOTIFY_DONE; | ||
107 | } | ||
108 | |||
80 | static inline void imx2_wdt_setup(struct watchdog_device *wdog) | 109 | static inline void imx2_wdt_setup(struct watchdog_device *wdog) |
81 | { | 110 | { |
82 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 111 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
@@ -191,12 +220,10 @@ static struct regmap_config imx2_wdt_regmap_config = { | |||
191 | 220 | ||
192 | static int __init imx2_wdt_probe(struct platform_device *pdev) | 221 | static int __init imx2_wdt_probe(struct platform_device *pdev) |
193 | { | 222 | { |
194 | struct device_node *np = pdev->dev.of_node; | ||
195 | struct imx2_wdt_device *wdev; | 223 | struct imx2_wdt_device *wdev; |
196 | struct watchdog_device *wdog; | 224 | struct watchdog_device *wdog; |
197 | struct resource *res; | 225 | struct resource *res; |
198 | void __iomem *base; | 226 | void __iomem *base; |
199 | bool big_endian; | ||
200 | int ret; | 227 | int ret; |
201 | u32 val; | 228 | u32 val; |
202 | 229 | ||
@@ -204,10 +231,6 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
204 | if (!wdev) | 231 | if (!wdev) |
205 | return -ENOMEM; | 232 | return -ENOMEM; |
206 | 233 | ||
207 | big_endian = of_property_read_bool(np, "big-endian"); | ||
208 | if (big_endian) | ||
209 | imx2_wdt_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; | ||
210 | |||
211 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 234 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
212 | base = devm_ioremap_resource(&pdev->dev, res); | 235 | base = devm_ioremap_resource(&pdev->dev, res); |
213 | if (IS_ERR(base)) | 236 | if (IS_ERR(base)) |
@@ -257,6 +280,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) | |||
257 | return ret; | 280 | return ret; |
258 | } | 281 | } |
259 | 282 | ||
283 | wdev->restart_handler.notifier_call = imx2_restart_handler; | ||
284 | wdev->restart_handler.priority = 128; | ||
285 | ret = register_restart_handler(&wdev->restart_handler); | ||
286 | if (ret) | ||
287 | dev_err(&pdev->dev, "cannot register restart handler\n"); | ||
288 | |||
260 | dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", | 289 | dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", |
261 | wdog->timeout, nowayout); | 290 | wdog->timeout, nowayout); |
262 | 291 | ||
@@ -268,6 +297,8 @@ static int __exit imx2_wdt_remove(struct platform_device *pdev) | |||
268 | struct watchdog_device *wdog = platform_get_drvdata(pdev); | 297 | struct watchdog_device *wdog = platform_get_drvdata(pdev); |
269 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); | 298 | struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); |
270 | 299 | ||
300 | unregister_restart_handler(&wdev->restart_handler); | ||
301 | |||
271 | watchdog_unregister_device(wdog); | 302 | watchdog_unregister_device(wdog); |
272 | 303 | ||
273 | if (imx2_wdt_is_running(wdev)) { | 304 | if (imx2_wdt_is_running(wdev)) { |
diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c new file mode 100644 index 000000000000..ef6a298e8c45 --- /dev/null +++ b/drivers/watchdog/meson_wdt.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | * Meson Watchdog Driver | ||
3 | * | ||
4 | * Copyright (c) 2014 Carlo Caione | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/err.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/moduleparam.h> | ||
20 | #include <linux/notifier.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/reboot.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/watchdog.h> | ||
26 | |||
27 | #define DRV_NAME "meson_wdt" | ||
28 | |||
29 | #define MESON_WDT_TC 0x00 | ||
30 | #define MESON_WDT_TC_EN BIT(22) | ||
31 | #define MESON_WDT_TC_TM_MASK 0x3fffff | ||
32 | #define MESON_WDT_DC_RESET (3 << 24) | ||
33 | |||
34 | #define MESON_WDT_RESET 0x04 | ||
35 | |||
36 | #define MESON_WDT_TIMEOUT 30 | ||
37 | #define MESON_WDT_MIN_TIMEOUT 1 | ||
38 | #define MESON_WDT_MAX_TIMEOUT (MESON_WDT_TC_TM_MASK / 100000) | ||
39 | |||
40 | #define MESON_SEC_TO_TC(s) ((s) * 100000) | ||
41 | |||
42 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
43 | static unsigned int timeout = MESON_WDT_TIMEOUT; | ||
44 | |||
45 | struct meson_wdt_dev { | ||
46 | struct watchdog_device wdt_dev; | ||
47 | void __iomem *wdt_base; | ||
48 | struct notifier_block restart_handler; | ||
49 | }; | ||
50 | |||
51 | static int meson_restart_handle(struct notifier_block *this, unsigned long mode, | ||
52 | void *cmd) | ||
53 | { | ||
54 | u32 tc_reboot = MESON_WDT_DC_RESET | MESON_WDT_TC_EN; | ||
55 | struct meson_wdt_dev *meson_wdt = container_of(this, | ||
56 | struct meson_wdt_dev, | ||
57 | restart_handler); | ||
58 | |||
59 | while (1) { | ||
60 | writel(tc_reboot, meson_wdt->wdt_base + MESON_WDT_TC); | ||
61 | mdelay(5); | ||
62 | } | ||
63 | |||
64 | return NOTIFY_DONE; | ||
65 | } | ||
66 | |||
67 | static int meson_wdt_ping(struct watchdog_device *wdt_dev) | ||
68 | { | ||
69 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); | ||
70 | |||
71 | writel(0, meson_wdt->wdt_base + MESON_WDT_RESET); | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static void meson_wdt_change_timeout(struct watchdog_device *wdt_dev, | ||
77 | unsigned int timeout) | ||
78 | { | ||
79 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); | ||
80 | u32 reg; | ||
81 | |||
82 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); | ||
83 | reg &= ~MESON_WDT_TC_TM_MASK; | ||
84 | reg |= MESON_SEC_TO_TC(timeout); | ||
85 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); | ||
86 | } | ||
87 | |||
88 | static int meson_wdt_set_timeout(struct watchdog_device *wdt_dev, | ||
89 | unsigned int timeout) | ||
90 | { | ||
91 | wdt_dev->timeout = timeout; | ||
92 | |||
93 | meson_wdt_change_timeout(wdt_dev, timeout); | ||
94 | meson_wdt_ping(wdt_dev); | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static int meson_wdt_stop(struct watchdog_device *wdt_dev) | ||
100 | { | ||
101 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); | ||
102 | u32 reg; | ||
103 | |||
104 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); | ||
105 | reg &= ~MESON_WDT_TC_EN; | ||
106 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int meson_wdt_start(struct watchdog_device *wdt_dev) | ||
112 | { | ||
113 | struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); | ||
114 | u32 reg; | ||
115 | |||
116 | meson_wdt_change_timeout(wdt_dev, meson_wdt->wdt_dev.timeout); | ||
117 | meson_wdt_ping(wdt_dev); | ||
118 | |||
119 | reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); | ||
120 | reg |= MESON_WDT_TC_EN; | ||
121 | writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static const struct watchdog_info meson_wdt_info = { | ||
127 | .identity = DRV_NAME, | ||
128 | .options = WDIOF_SETTIMEOUT | | ||
129 | WDIOF_KEEPALIVEPING | | ||
130 | WDIOF_MAGICCLOSE, | ||
131 | }; | ||
132 | |||
133 | static const struct watchdog_ops meson_wdt_ops = { | ||
134 | .owner = THIS_MODULE, | ||
135 | .start = meson_wdt_start, | ||
136 | .stop = meson_wdt_stop, | ||
137 | .ping = meson_wdt_ping, | ||
138 | .set_timeout = meson_wdt_set_timeout, | ||
139 | }; | ||
140 | |||
141 | static int meson_wdt_probe(struct platform_device *pdev) | ||
142 | { | ||
143 | struct resource *res; | ||
144 | struct meson_wdt_dev *meson_wdt; | ||
145 | int err; | ||
146 | |||
147 | meson_wdt = devm_kzalloc(&pdev->dev, sizeof(*meson_wdt), GFP_KERNEL); | ||
148 | if (!meson_wdt) | ||
149 | return -ENOMEM; | ||
150 | |||
151 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
152 | meson_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); | ||
153 | if (IS_ERR(meson_wdt->wdt_base)) | ||
154 | return PTR_ERR(meson_wdt->wdt_base); | ||
155 | |||
156 | meson_wdt->wdt_dev.parent = &pdev->dev; | ||
157 | meson_wdt->wdt_dev.info = &meson_wdt_info; | ||
158 | meson_wdt->wdt_dev.ops = &meson_wdt_ops; | ||
159 | meson_wdt->wdt_dev.timeout = MESON_WDT_TIMEOUT; | ||
160 | meson_wdt->wdt_dev.max_timeout = MESON_WDT_MAX_TIMEOUT; | ||
161 | meson_wdt->wdt_dev.min_timeout = MESON_WDT_MIN_TIMEOUT; | ||
162 | |||
163 | watchdog_set_drvdata(&meson_wdt->wdt_dev, meson_wdt); | ||
164 | |||
165 | watchdog_init_timeout(&meson_wdt->wdt_dev, timeout, &pdev->dev); | ||
166 | watchdog_set_nowayout(&meson_wdt->wdt_dev, nowayout); | ||
167 | |||
168 | meson_wdt_stop(&meson_wdt->wdt_dev); | ||
169 | |||
170 | err = watchdog_register_device(&meson_wdt->wdt_dev); | ||
171 | if (err) | ||
172 | return err; | ||
173 | |||
174 | platform_set_drvdata(pdev, meson_wdt); | ||
175 | |||
176 | meson_wdt->restart_handler.notifier_call = meson_restart_handle; | ||
177 | meson_wdt->restart_handler.priority = 128; | ||
178 | err = register_restart_handler(&meson_wdt->restart_handler); | ||
179 | if (err) | ||
180 | dev_err(&pdev->dev, | ||
181 | "cannot register restart handler (err=%d)\n", err); | ||
182 | |||
183 | dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", | ||
184 | meson_wdt->wdt_dev.timeout, nowayout); | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int meson_wdt_remove(struct platform_device *pdev) | ||
190 | { | ||
191 | struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); | ||
192 | |||
193 | unregister_restart_handler(&meson_wdt->restart_handler); | ||
194 | |||
195 | watchdog_unregister_device(&meson_wdt->wdt_dev); | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static void meson_wdt_shutdown(struct platform_device *pdev) | ||
201 | { | ||
202 | struct meson_wdt_dev *meson_wdt = platform_get_drvdata(pdev); | ||
203 | |||
204 | meson_wdt_stop(&meson_wdt->wdt_dev); | ||
205 | } | ||
206 | |||
207 | static const struct of_device_id meson_wdt_dt_ids[] = { | ||
208 | { .compatible = "amlogic,meson6-wdt" }, | ||
209 | { /* sentinel */ } | ||
210 | }; | ||
211 | MODULE_DEVICE_TABLE(of, meson_wdt_dt_ids); | ||
212 | |||
213 | static struct platform_driver meson_wdt_driver = { | ||
214 | .probe = meson_wdt_probe, | ||
215 | .remove = meson_wdt_remove, | ||
216 | .shutdown = meson_wdt_shutdown, | ||
217 | .driver = { | ||
218 | .owner = THIS_MODULE, | ||
219 | .name = DRV_NAME, | ||
220 | .of_match_table = meson_wdt_dt_ids, | ||
221 | }, | ||
222 | }; | ||
223 | |||
224 | module_platform_driver(meson_wdt_driver); | ||
225 | |||
226 | module_param(timeout, uint, 0); | ||
227 | MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); | ||
228 | |||
229 | module_param(nowayout, bool, 0); | ||
230 | MODULE_PARM_DESC(nowayout, | ||
231 | "Watchdog cannot be stopped once started (default=" | ||
232 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
233 | |||
234 | MODULE_LICENSE("GPL"); | ||
235 | MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); | ||
236 | MODULE_DESCRIPTION("Meson Watchdog Timer Driver"); | ||
diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c index 1e6e28df5d7b..b2e1b4cbbdc1 100644 --- a/drivers/watchdog/of_xilinx_wdt.c +++ b/drivers/watchdog/of_xilinx_wdt.c | |||
@@ -236,7 +236,6 @@ static struct platform_driver xwdt_driver = { | |||
236 | .probe = xwdt_probe, | 236 | .probe = xwdt_probe, |
237 | .remove = xwdt_remove, | 237 | .remove = xwdt_remove, |
238 | .driver = { | 238 | .driver = { |
239 | .owner = THIS_MODULE, | ||
240 | .name = WATCHDOG_NAME, | 239 | .name = WATCHDOG_NAME, |
241 | .of_match_table = xwdt_of_match, | 240 | .of_match_table = xwdt_of_match, |
242 | }, | 241 | }, |
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c new file mode 100644 index 000000000000..aa85618c4d03 --- /dev/null +++ b/drivers/watchdog/qcom-wdt.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* Copyright (c) 2014, The Linux Foundation. All rights reserved. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 and | ||
5 | * only version 2 as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | * | ||
12 | */ | ||
13 | #include <linux/clk.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/reboot.h> | ||
21 | #include <linux/watchdog.h> | ||
22 | |||
23 | #define WDT_RST 0x0 | ||
24 | #define WDT_EN 0x8 | ||
25 | #define WDT_BITE_TIME 0x24 | ||
26 | |||
27 | struct qcom_wdt { | ||
28 | struct watchdog_device wdd; | ||
29 | struct clk *clk; | ||
30 | unsigned long rate; | ||
31 | struct notifier_block restart_nb; | ||
32 | void __iomem *base; | ||
33 | }; | ||
34 | |||
35 | static inline | ||
36 | struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd) | ||
37 | { | ||
38 | return container_of(wdd, struct qcom_wdt, wdd); | ||
39 | } | ||
40 | |||
41 | static int qcom_wdt_start(struct watchdog_device *wdd) | ||
42 | { | ||
43 | struct qcom_wdt *wdt = to_qcom_wdt(wdd); | ||
44 | |||
45 | writel(0, wdt->base + WDT_EN); | ||
46 | writel(1, wdt->base + WDT_RST); | ||
47 | writel(wdd->timeout * wdt->rate, wdt->base + WDT_BITE_TIME); | ||
48 | writel(1, wdt->base + WDT_EN); | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static int qcom_wdt_stop(struct watchdog_device *wdd) | ||
53 | { | ||
54 | struct qcom_wdt *wdt = to_qcom_wdt(wdd); | ||
55 | |||
56 | writel(0, wdt->base + WDT_EN); | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | static int qcom_wdt_ping(struct watchdog_device *wdd) | ||
61 | { | ||
62 | struct qcom_wdt *wdt = to_qcom_wdt(wdd); | ||
63 | |||
64 | writel(1, wdt->base + WDT_RST); | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int qcom_wdt_set_timeout(struct watchdog_device *wdd, | ||
69 | unsigned int timeout) | ||
70 | { | ||
71 | wdd->timeout = timeout; | ||
72 | return qcom_wdt_start(wdd); | ||
73 | } | ||
74 | |||
75 | static const struct watchdog_ops qcom_wdt_ops = { | ||
76 | .start = qcom_wdt_start, | ||
77 | .stop = qcom_wdt_stop, | ||
78 | .ping = qcom_wdt_ping, | ||
79 | .set_timeout = qcom_wdt_set_timeout, | ||
80 | .owner = THIS_MODULE, | ||
81 | }; | ||
82 | |||
83 | static const struct watchdog_info qcom_wdt_info = { | ||
84 | .options = WDIOF_KEEPALIVEPING | ||
85 | | WDIOF_MAGICCLOSE | ||
86 | | WDIOF_SETTIMEOUT, | ||
87 | .identity = KBUILD_MODNAME, | ||
88 | }; | ||
89 | |||
90 | static int qcom_wdt_restart(struct notifier_block *nb, unsigned long action, | ||
91 | void *data) | ||
92 | { | ||
93 | struct qcom_wdt *wdt = container_of(nb, struct qcom_wdt, restart_nb); | ||
94 | u32 timeout; | ||
95 | |||
96 | /* | ||
97 | * Trigger watchdog bite: | ||
98 | * Setup BITE_TIME to be 128ms, and enable WDT. | ||
99 | */ | ||
100 | timeout = 128 * wdt->rate / 1000; | ||
101 | |||
102 | writel(0, wdt->base + WDT_EN); | ||
103 | writel(1, wdt->base + WDT_RST); | ||
104 | writel(timeout, wdt->base + WDT_BITE_TIME); | ||
105 | writel(1, wdt->base + WDT_EN); | ||
106 | |||
107 | /* | ||
108 | * Actually make sure the above sequence hits hardware before sleeping. | ||
109 | */ | ||
110 | wmb(); | ||
111 | |||
112 | msleep(150); | ||
113 | return NOTIFY_DONE; | ||
114 | } | ||
115 | |||
116 | static int qcom_wdt_probe(struct platform_device *pdev) | ||
117 | { | ||
118 | struct qcom_wdt *wdt; | ||
119 | struct resource *res; | ||
120 | int ret; | ||
121 | |||
122 | wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); | ||
123 | if (!wdt) | ||
124 | return -ENOMEM; | ||
125 | |||
126 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
127 | wdt->base = devm_ioremap_resource(&pdev->dev, res); | ||
128 | if (IS_ERR(wdt->base)) | ||
129 | return PTR_ERR(wdt->base); | ||
130 | |||
131 | wdt->clk = devm_clk_get(&pdev->dev, NULL); | ||
132 | if (IS_ERR(wdt->clk)) { | ||
133 | dev_err(&pdev->dev, "failed to get input clock\n"); | ||
134 | return PTR_ERR(wdt->clk); | ||
135 | } | ||
136 | |||
137 | ret = clk_prepare_enable(wdt->clk); | ||
138 | if (ret) { | ||
139 | dev_err(&pdev->dev, "failed to setup clock\n"); | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * We use the clock rate to calculate the max timeout, so ensure it's | ||
145 | * not zero to avoid a divide-by-zero exception. | ||
146 | * | ||
147 | * WATCHDOG_CORE assumes units of seconds, if the WDT is clocked such | ||
148 | * that it would bite before a second elapses it's usefulness is | ||
149 | * limited. Bail if this is the case. | ||
150 | */ | ||
151 | wdt->rate = clk_get_rate(wdt->clk); | ||
152 | if (wdt->rate == 0 || | ||
153 | wdt->rate > 0x10000000U) { | ||
154 | dev_err(&pdev->dev, "invalid clock rate\n"); | ||
155 | ret = -EINVAL; | ||
156 | goto err_clk_unprepare; | ||
157 | } | ||
158 | |||
159 | wdt->wdd.dev = &pdev->dev; | ||
160 | wdt->wdd.info = &qcom_wdt_info; | ||
161 | wdt->wdd.ops = &qcom_wdt_ops; | ||
162 | wdt->wdd.min_timeout = 1; | ||
163 | wdt->wdd.max_timeout = 0x10000000U / wdt->rate; | ||
164 | |||
165 | /* | ||
166 | * If 'timeout-sec' unspecified in devicetree, assume a 30 second | ||
167 | * default, unless the max timeout is less than 30 seconds, then use | ||
168 | * the max instead. | ||
169 | */ | ||
170 | wdt->wdd.timeout = min(wdt->wdd.max_timeout, 30U); | ||
171 | watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev); | ||
172 | |||
173 | ret = watchdog_register_device(&wdt->wdd); | ||
174 | if (ret) { | ||
175 | dev_err(&pdev->dev, "failed to register watchdog\n"); | ||
176 | goto err_clk_unprepare; | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * WDT restart notifier has priority 0 (use as a last resort) | ||
181 | */ | ||
182 | wdt->restart_nb.notifier_call = qcom_wdt_restart; | ||
183 | ret = register_restart_handler(&wdt->restart_nb); | ||
184 | if (ret) | ||
185 | dev_err(&pdev->dev, "failed to setup restart handler\n"); | ||
186 | |||
187 | platform_set_drvdata(pdev, wdt); | ||
188 | return 0; | ||
189 | |||
190 | err_clk_unprepare: | ||
191 | clk_disable_unprepare(wdt->clk); | ||
192 | return ret; | ||
193 | } | ||
194 | |||
195 | static int qcom_wdt_remove(struct platform_device *pdev) | ||
196 | { | ||
197 | struct qcom_wdt *wdt = platform_get_drvdata(pdev); | ||
198 | |||
199 | unregister_restart_handler(&wdt->restart_nb); | ||
200 | watchdog_unregister_device(&wdt->wdd); | ||
201 | clk_disable_unprepare(wdt->clk); | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static const struct of_device_id qcom_wdt_of_table[] = { | ||
206 | { .compatible = "qcom,kpss-wdt-msm8960", }, | ||
207 | { .compatible = "qcom,kpss-wdt-apq8064", }, | ||
208 | { .compatible = "qcom,kpss-wdt-ipq8064", }, | ||
209 | { }, | ||
210 | }; | ||
211 | MODULE_DEVICE_TABLE(of, qcom_wdt_of_table); | ||
212 | |||
213 | static struct platform_driver qcom_watchdog_driver = { | ||
214 | .probe = qcom_wdt_probe, | ||
215 | .remove = qcom_wdt_remove, | ||
216 | .driver = { | ||
217 | .name = KBUILD_MODNAME, | ||
218 | .of_match_table = qcom_wdt_of_table, | ||
219 | }, | ||
220 | }; | ||
221 | module_platform_driver(qcom_watchdog_driver); | ||
222 | |||
223 | MODULE_DESCRIPTION("QCOM KPSS Watchdog Driver"); | ||
224 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/watchdog/rn5t618_wdt.c b/drivers/watchdog/rn5t618_wdt.c new file mode 100644 index 000000000000..d1c12278cb6a --- /dev/null +++ b/drivers/watchdog/rn5t618_wdt.c | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | * Watchdog driver for Ricoh RN5T618 PMIC | ||
3 | * | ||
4 | * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * version 2 as published by the Free Software Foundation. | ||
9 | * | ||
10 | * You should have received a copy of the GNU General Public License | ||
11 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
12 | */ | ||
13 | |||
14 | #include <linux/device.h> | ||
15 | #include <linux/mfd/rn5t618.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/watchdog.h> | ||
19 | |||
20 | #define DRIVER_NAME "rn5t618-wdt" | ||
21 | |||
22 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
23 | static unsigned int timeout; | ||
24 | |||
25 | module_param(timeout, uint, 0); | ||
26 | MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds"); | ||
27 | |||
28 | module_param(nowayout, bool, 0); | ||
29 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
30 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
31 | |||
32 | struct rn5t618_wdt { | ||
33 | struct watchdog_device wdt_dev; | ||
34 | struct rn5t618 *rn5t618; | ||
35 | }; | ||
36 | |||
37 | /* | ||
38 | * This array encodes the values of WDOGTIM field for the supported | ||
39 | * watchdog expiration times. If the watchdog is not accessed before | ||
40 | * the timer expiration, the PMU generates an interrupt and if the CPU | ||
41 | * doesn't clear it within one second the system is restarted. | ||
42 | */ | ||
43 | static const struct { | ||
44 | u8 reg_val; | ||
45 | unsigned int time; | ||
46 | } rn5t618_wdt_map[] = { | ||
47 | { 0, 1 }, | ||
48 | { 1, 8 }, | ||
49 | { 2, 32 }, | ||
50 | { 3, 128 }, | ||
51 | }; | ||
52 | |||
53 | static int rn5t618_wdt_set_timeout(struct watchdog_device *wdt_dev, | ||
54 | unsigned int t) | ||
55 | { | ||
56 | struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); | ||
57 | int ret, i; | ||
58 | |||
59 | for (i = 0; i < ARRAY_SIZE(rn5t618_wdt_map); i++) { | ||
60 | if (rn5t618_wdt_map[i].time + 1 >= t) | ||
61 | break; | ||
62 | } | ||
63 | |||
64 | if (i == ARRAY_SIZE(rn5t618_wdt_map)) | ||
65 | return -EINVAL; | ||
66 | |||
67 | ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, | ||
68 | RN5T618_WATCHDOG_WDOGTIM_M, | ||
69 | rn5t618_wdt_map[i].reg_val); | ||
70 | if (!ret) | ||
71 | wdt_dev->timeout = rn5t618_wdt_map[i].time; | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static int rn5t618_wdt_start(struct watchdog_device *wdt_dev) | ||
77 | { | ||
78 | struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); | ||
79 | int ret; | ||
80 | |||
81 | ret = rn5t618_wdt_set_timeout(wdt_dev, wdt_dev->timeout); | ||
82 | if (ret) | ||
83 | return ret; | ||
84 | |||
85 | /* enable repower-on */ | ||
86 | ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_REPCNT, | ||
87 | RN5T618_REPCNT_REPWRON, | ||
88 | RN5T618_REPCNT_REPWRON); | ||
89 | if (ret) | ||
90 | return ret; | ||
91 | |||
92 | /* enable watchdog */ | ||
93 | ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, | ||
94 | RN5T618_WATCHDOG_WDOGEN, | ||
95 | RN5T618_WATCHDOG_WDOGEN); | ||
96 | if (ret) | ||
97 | return ret; | ||
98 | |||
99 | /* enable watchdog interrupt */ | ||
100 | return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIREN, | ||
101 | RN5T618_PWRIRQ_IR_WDOG, | ||
102 | RN5T618_PWRIRQ_IR_WDOG); | ||
103 | } | ||
104 | |||
105 | static int rn5t618_wdt_stop(struct watchdog_device *wdt_dev) | ||
106 | { | ||
107 | struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); | ||
108 | |||
109 | return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG, | ||
110 | RN5T618_WATCHDOG_WDOGEN, 0); | ||
111 | } | ||
112 | |||
113 | static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev) | ||
114 | { | ||
115 | struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev); | ||
116 | unsigned int val; | ||
117 | int ret; | ||
118 | |||
119 | /* The counter is restarted after a R/W access to watchdog register */ | ||
120 | ret = regmap_read(wdt->rn5t618->regmap, RN5T618_WATCHDOG, &val); | ||
121 | if (ret) | ||
122 | return ret; | ||
123 | |||
124 | ret = regmap_write(wdt->rn5t618->regmap, RN5T618_WATCHDOG, val); | ||
125 | if (ret) | ||
126 | return ret; | ||
127 | |||
128 | /* Clear pending watchdog interrupt */ | ||
129 | return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIRQ, | ||
130 | RN5T618_PWRIRQ_IR_WDOG, 0); | ||
131 | } | ||
132 | |||
133 | static struct watchdog_info rn5t618_wdt_info = { | ||
134 | .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | | ||
135 | WDIOF_KEEPALIVEPING, | ||
136 | .identity = DRIVER_NAME, | ||
137 | }; | ||
138 | |||
139 | static struct watchdog_ops rn5t618_wdt_ops = { | ||
140 | .owner = THIS_MODULE, | ||
141 | .start = rn5t618_wdt_start, | ||
142 | .stop = rn5t618_wdt_stop, | ||
143 | .ping = rn5t618_wdt_ping, | ||
144 | .set_timeout = rn5t618_wdt_set_timeout, | ||
145 | }; | ||
146 | |||
147 | static int rn5t618_wdt_probe(struct platform_device *pdev) | ||
148 | { | ||
149 | struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent); | ||
150 | struct rn5t618_wdt *wdt; | ||
151 | int min_timeout, max_timeout; | ||
152 | |||
153 | wdt = devm_kzalloc(&pdev->dev, sizeof(struct rn5t618_wdt), GFP_KERNEL); | ||
154 | if (!wdt) | ||
155 | return -ENOMEM; | ||
156 | |||
157 | min_timeout = rn5t618_wdt_map[0].time; | ||
158 | max_timeout = rn5t618_wdt_map[ARRAY_SIZE(rn5t618_wdt_map) - 1].time; | ||
159 | |||
160 | wdt->rn5t618 = rn5t618; | ||
161 | wdt->wdt_dev.info = &rn5t618_wdt_info; | ||
162 | wdt->wdt_dev.ops = &rn5t618_wdt_ops; | ||
163 | wdt->wdt_dev.min_timeout = min_timeout; | ||
164 | wdt->wdt_dev.max_timeout = max_timeout; | ||
165 | wdt->wdt_dev.timeout = max_timeout; | ||
166 | wdt->wdt_dev.parent = &pdev->dev; | ||
167 | |||
168 | watchdog_set_drvdata(&wdt->wdt_dev, wdt); | ||
169 | watchdog_init_timeout(&wdt->wdt_dev, timeout, &pdev->dev); | ||
170 | watchdog_set_nowayout(&wdt->wdt_dev, nowayout); | ||
171 | |||
172 | platform_set_drvdata(pdev, wdt); | ||
173 | |||
174 | return watchdog_register_device(&wdt->wdt_dev); | ||
175 | } | ||
176 | |||
177 | static int rn5t618_wdt_remove(struct platform_device *pdev) | ||
178 | { | ||
179 | struct rn5t618_wdt *wdt = platform_get_drvdata(pdev); | ||
180 | |||
181 | watchdog_unregister_device(&wdt->wdt_dev); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static struct platform_driver rn5t618_wdt_driver = { | ||
187 | .probe = rn5t618_wdt_probe, | ||
188 | .remove = rn5t618_wdt_remove, | ||
189 | .driver = { | ||
190 | .name = DRIVER_NAME, | ||
191 | }, | ||
192 | }; | ||
193 | |||
194 | module_platform_driver(rn5t618_wdt_driver); | ||
195 | |||
196 | MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); | ||
197 | MODULE_DESCRIPTION("RN5T618 watchdog driver"); | ||
198 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 7c6ccd071baf..8532c3e2aea7 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c | |||
@@ -41,6 +41,8 @@ | |||
41 | #include <linux/of.h> | 41 | #include <linux/of.h> |
42 | #include <linux/mfd/syscon.h> | 42 | #include <linux/mfd/syscon.h> |
43 | #include <linux/regmap.h> | 43 | #include <linux/regmap.h> |
44 | #include <linux/reboot.h> | ||
45 | #include <linux/delay.h> | ||
44 | 46 | ||
45 | #define S3C2410_WTCON 0x00 | 47 | #define S3C2410_WTCON 0x00 |
46 | #define S3C2410_WTDAT 0x04 | 48 | #define S3C2410_WTDAT 0x04 |
@@ -128,6 +130,7 @@ struct s3c2410_wdt { | |||
128 | unsigned long wtdat_save; | 130 | unsigned long wtdat_save; |
129 | struct watchdog_device wdt_device; | 131 | struct watchdog_device wdt_device; |
130 | struct notifier_block freq_transition; | 132 | struct notifier_block freq_transition; |
133 | struct notifier_block restart_handler; | ||
131 | struct s3c2410_wdt_variant *drv_data; | 134 | struct s3c2410_wdt_variant *drv_data; |
132 | struct regmap *pmureg; | 135 | struct regmap *pmureg; |
133 | }; | 136 | }; |
@@ -155,6 +158,15 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = { | |||
155 | .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, | 158 | .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, |
156 | }; | 159 | }; |
157 | 160 | ||
161 | static const struct s3c2410_wdt_variant drv_data_exynos7 = { | ||
162 | .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, | ||
163 | .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, | ||
164 | .mask_bit = 0, | ||
165 | .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, | ||
166 | .rst_stat_bit = 23, /* A57 WDTRESET */ | ||
167 | .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, | ||
168 | }; | ||
169 | |||
158 | static const struct of_device_id s3c2410_wdt_match[] = { | 170 | static const struct of_device_id s3c2410_wdt_match[] = { |
159 | { .compatible = "samsung,s3c2410-wdt", | 171 | { .compatible = "samsung,s3c2410-wdt", |
160 | .data = &drv_data_s3c2410 }, | 172 | .data = &drv_data_s3c2410 }, |
@@ -162,6 +174,8 @@ static const struct of_device_id s3c2410_wdt_match[] = { | |||
162 | .data = &drv_data_exynos5250 }, | 174 | .data = &drv_data_exynos5250 }, |
163 | { .compatible = "samsung,exynos5420-wdt", | 175 | { .compatible = "samsung,exynos5420-wdt", |
164 | .data = &drv_data_exynos5420 }, | 176 | .data = &drv_data_exynos5420 }, |
177 | { .compatible = "samsung,exynos7-wdt", | ||
178 | .data = &drv_data_exynos7 }, | ||
165 | {}, | 179 | {}, |
166 | }; | 180 | }; |
167 | MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); | 181 | MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); |
@@ -438,6 +452,31 @@ static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) | |||
438 | } | 452 | } |
439 | #endif | 453 | #endif |
440 | 454 | ||
455 | static int s3c2410wdt_restart(struct notifier_block *this, | ||
456 | unsigned long mode, void *cmd) | ||
457 | { | ||
458 | struct s3c2410_wdt *wdt = container_of(this, struct s3c2410_wdt, | ||
459 | restart_handler); | ||
460 | void __iomem *wdt_base = wdt->reg_base; | ||
461 | |||
462 | /* disable watchdog, to be safe */ | ||
463 | writel(0, wdt_base + S3C2410_WTCON); | ||
464 | |||
465 | /* put initial values into count and data */ | ||
466 | writel(0x80, wdt_base + S3C2410_WTCNT); | ||
467 | writel(0x80, wdt_base + S3C2410_WTDAT); | ||
468 | |||
469 | /* set the watchdog to go and reset... */ | ||
470 | writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 | | ||
471 | S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20), | ||
472 | wdt_base + S3C2410_WTCON); | ||
473 | |||
474 | /* wait for reset to assert... */ | ||
475 | mdelay(500); | ||
476 | |||
477 | return NOTIFY_DONE; | ||
478 | } | ||
479 | |||
441 | static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) | 480 | static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) |
442 | { | 481 | { |
443 | unsigned int rst_stat; | 482 | unsigned int rst_stat; |
@@ -592,6 +631,12 @@ static int s3c2410wdt_probe(struct platform_device *pdev) | |||
592 | 631 | ||
593 | platform_set_drvdata(pdev, wdt); | 632 | platform_set_drvdata(pdev, wdt); |
594 | 633 | ||
634 | wdt->restart_handler.notifier_call = s3c2410wdt_restart; | ||
635 | wdt->restart_handler.priority = 128; | ||
636 | ret = register_restart_handler(&wdt->restart_handler); | ||
637 | if (ret) | ||
638 | pr_err("cannot register restart handler, %d\n", ret); | ||
639 | |||
595 | /* print out a statement of readiness */ | 640 | /* print out a statement of readiness */ |
596 | 641 | ||
597 | wtcon = readl(wdt->reg_base + S3C2410_WTCON); | 642 | wtcon = readl(wdt->reg_base + S3C2410_WTCON); |
@@ -621,6 +666,8 @@ static int s3c2410wdt_remove(struct platform_device *dev) | |||
621 | int ret; | 666 | int ret; |
622 | struct s3c2410_wdt *wdt = platform_get_drvdata(dev); | 667 | struct s3c2410_wdt *wdt = platform_get_drvdata(dev); |
623 | 668 | ||
669 | unregister_restart_handler(&wdt->restart_handler); | ||
670 | |||
624 | ret = s3c2410wdt_mask_and_disable_reset(wdt, true); | 671 | ret = s3c2410wdt_mask_and_disable_reset(wdt, true); |
625 | if (ret < 0) | 672 | if (ret < 0) |
626 | return ret; | 673 | return ret; |
diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c index 3804d5e9baea..a62b1b6decf4 100644 --- a/drivers/watchdog/stmp3xxx_rtc_wdt.c +++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c | |||
@@ -94,9 +94,33 @@ static int stmp3xxx_wdt_remove(struct platform_device *pdev) | |||
94 | return 0; | 94 | return 0; |
95 | } | 95 | } |
96 | 96 | ||
97 | static int __maybe_unused stmp3xxx_wdt_suspend(struct device *dev) | ||
98 | { | ||
99 | struct watchdog_device *wdd = &stmp3xxx_wdd; | ||
100 | |||
101 | if (watchdog_active(wdd)) | ||
102 | return wdt_stop(wdd); | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int __maybe_unused stmp3xxx_wdt_resume(struct device *dev) | ||
108 | { | ||
109 | struct watchdog_device *wdd = &stmp3xxx_wdd; | ||
110 | |||
111 | if (watchdog_active(wdd)) | ||
112 | return wdt_start(wdd); | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static SIMPLE_DEV_PM_OPS(stmp3xxx_wdt_pm_ops, | ||
118 | stmp3xxx_wdt_suspend, stmp3xxx_wdt_resume); | ||
119 | |||
97 | static struct platform_driver stmp3xxx_wdt_driver = { | 120 | static struct platform_driver stmp3xxx_wdt_driver = { |
98 | .driver = { | 121 | .driver = { |
99 | .name = "stmp3xxx_rtc_wdt", | 122 | .name = "stmp3xxx_rtc_wdt", |
123 | .pm = &stmp3xxx_wdt_pm_ops, | ||
100 | }, | 124 | }, |
101 | .probe = stmp3xxx_wdt_probe, | 125 | .probe = stmp3xxx_wdt_probe, |
102 | .remove = stmp3xxx_wdt_remove, | 126 | .remove = stmp3xxx_wdt_remove, |
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 480bb557f353..b62301e74e5f 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/moduleparam.h> | 23 | #include <linux/moduleparam.h> |
24 | #include <linux/notifier.h> | 24 | #include <linux/notifier.h> |
25 | #include <linux/of.h> | 25 | #include <linux/of.h> |
26 | #include <linux/of_device.h> | ||
26 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
27 | #include <linux/reboot.h> | 28 | #include <linux/reboot.h> |
28 | #include <linux/types.h> | 29 | #include <linux/types.h> |
@@ -30,15 +31,11 @@ | |||
30 | 31 | ||
31 | #define WDT_MAX_TIMEOUT 16 | 32 | #define WDT_MAX_TIMEOUT 16 |
32 | #define WDT_MIN_TIMEOUT 1 | 33 | #define WDT_MIN_TIMEOUT 1 |
33 | #define WDT_MODE_TIMEOUT(n) ((n) << 3) | 34 | #define WDT_TIMEOUT_MASK 0x0F |
34 | #define WDT_TIMEOUT_MASK WDT_MODE_TIMEOUT(0x0F) | ||
35 | 35 | ||
36 | #define WDT_CTRL 0x00 | ||
37 | #define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) | 36 | #define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) |
38 | 37 | ||
39 | #define WDT_MODE 0x04 | ||
40 | #define WDT_MODE_EN (1 << 0) | 38 | #define WDT_MODE_EN (1 << 0) |
41 | #define WDT_MODE_RST_EN (1 << 1) | ||
42 | 39 | ||
43 | #define DRV_NAME "sunxi-wdt" | 40 | #define DRV_NAME "sunxi-wdt" |
44 | #define DRV_VERSION "1.0" | 41 | #define DRV_VERSION "1.0" |
@@ -46,15 +43,29 @@ | |||
46 | static bool nowayout = WATCHDOG_NOWAYOUT; | 43 | static bool nowayout = WATCHDOG_NOWAYOUT; |
47 | static unsigned int timeout = WDT_MAX_TIMEOUT; | 44 | static unsigned int timeout = WDT_MAX_TIMEOUT; |
48 | 45 | ||
46 | /* | ||
47 | * This structure stores the register offsets for different variants | ||
48 | * of Allwinner's watchdog hardware. | ||
49 | */ | ||
50 | struct sunxi_wdt_reg { | ||
51 | u8 wdt_ctrl; | ||
52 | u8 wdt_cfg; | ||
53 | u8 wdt_mode; | ||
54 | u8 wdt_timeout_shift; | ||
55 | u8 wdt_reset_mask; | ||
56 | u8 wdt_reset_val; | ||
57 | }; | ||
58 | |||
49 | struct sunxi_wdt_dev { | 59 | struct sunxi_wdt_dev { |
50 | struct watchdog_device wdt_dev; | 60 | struct watchdog_device wdt_dev; |
51 | void __iomem *wdt_base; | 61 | void __iomem *wdt_base; |
62 | const struct sunxi_wdt_reg *wdt_regs; | ||
52 | struct notifier_block restart_handler; | 63 | struct notifier_block restart_handler; |
53 | }; | 64 | }; |
54 | 65 | ||
55 | /* | 66 | /* |
56 | * wdt_timeout_map maps the watchdog timer interval value in seconds to | 67 | * wdt_timeout_map maps the watchdog timer interval value in seconds to |
57 | * the value of the register WDT_MODE bit 3:6 | 68 | * the value of the register WDT_MODE at bits .wdt_timeout_shift ~ +3 |
58 | * | 69 | * |
59 | * [timeout seconds] = register value | 70 | * [timeout seconds] = register value |
60 | * | 71 | * |
@@ -82,19 +93,32 @@ static int sunxi_restart_handle(struct notifier_block *this, unsigned long mode, | |||
82 | struct sunxi_wdt_dev, | 93 | struct sunxi_wdt_dev, |
83 | restart_handler); | 94 | restart_handler); |
84 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 95 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
96 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | ||
97 | u32 val; | ||
98 | |||
99 | /* Set system reset function */ | ||
100 | val = readl(wdt_base + regs->wdt_cfg); | ||
101 | val &= ~(regs->wdt_reset_mask); | ||
102 | val |= regs->wdt_reset_val; | ||
103 | writel(val, wdt_base + regs->wdt_cfg); | ||
85 | 104 | ||
86 | /* Enable timer and set reset bit in the watchdog */ | 105 | /* Set lowest timeout and enable watchdog */ |
87 | writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); | 106 | val = readl(wdt_base + regs->wdt_mode); |
107 | val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); | ||
108 | val |= WDT_MODE_EN; | ||
109 | writel(val, wdt_base + regs->wdt_mode); | ||
88 | 110 | ||
89 | /* | 111 | /* |
90 | * Restart the watchdog. The default (and lowest) interval | 112 | * Restart the watchdog. The default (and lowest) interval |
91 | * value for the watchdog is 0.5s. | 113 | * value for the watchdog is 0.5s. |
92 | */ | 114 | */ |
93 | writel(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); | 115 | writel(WDT_CTRL_RELOAD, wdt_base + regs->wdt_ctrl); |
94 | 116 | ||
95 | while (1) { | 117 | while (1) { |
96 | mdelay(5); | 118 | mdelay(5); |
97 | writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); | 119 | val = readl(wdt_base + regs->wdt_mode); |
120 | val |= WDT_MODE_EN; | ||
121 | writel(val, wdt_base + regs->wdt_mode); | ||
98 | } | 122 | } |
99 | return NOTIFY_DONE; | 123 | return NOTIFY_DONE; |
100 | } | 124 | } |
@@ -103,8 +127,9 @@ static int sunxi_wdt_ping(struct watchdog_device *wdt_dev) | |||
103 | { | 127 | { |
104 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); | 128 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); |
105 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 129 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
130 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | ||
106 | 131 | ||
107 | iowrite32(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); | 132 | writel(WDT_CTRL_RELOAD, wdt_base + regs->wdt_ctrl); |
108 | 133 | ||
109 | return 0; | 134 | return 0; |
110 | } | 135 | } |
@@ -114,6 +139,7 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, | |||
114 | { | 139 | { |
115 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); | 140 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); |
116 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 141 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
142 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | ||
117 | u32 reg; | 143 | u32 reg; |
118 | 144 | ||
119 | if (wdt_timeout_map[timeout] == 0) | 145 | if (wdt_timeout_map[timeout] == 0) |
@@ -121,10 +147,10 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, | |||
121 | 147 | ||
122 | sunxi_wdt->wdt_dev.timeout = timeout; | 148 | sunxi_wdt->wdt_dev.timeout = timeout; |
123 | 149 | ||
124 | reg = ioread32(wdt_base + WDT_MODE); | 150 | reg = readl(wdt_base + regs->wdt_mode); |
125 | reg &= ~WDT_TIMEOUT_MASK; | 151 | reg &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); |
126 | reg |= WDT_MODE_TIMEOUT(wdt_timeout_map[timeout]); | 152 | reg |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift; |
127 | iowrite32(reg, wdt_base + WDT_MODE); | 153 | writel(reg, wdt_base + regs->wdt_mode); |
128 | 154 | ||
129 | sunxi_wdt_ping(wdt_dev); | 155 | sunxi_wdt_ping(wdt_dev); |
130 | 156 | ||
@@ -135,8 +161,9 @@ static int sunxi_wdt_stop(struct watchdog_device *wdt_dev) | |||
135 | { | 161 | { |
136 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); | 162 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); |
137 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 163 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
164 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | ||
138 | 165 | ||
139 | iowrite32(0, wdt_base + WDT_MODE); | 166 | writel(0, wdt_base + regs->wdt_mode); |
140 | 167 | ||
141 | return 0; | 168 | return 0; |
142 | } | 169 | } |
@@ -146,6 +173,7 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) | |||
146 | u32 reg; | 173 | u32 reg; |
147 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); | 174 | struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); |
148 | void __iomem *wdt_base = sunxi_wdt->wdt_base; | 175 | void __iomem *wdt_base = sunxi_wdt->wdt_base; |
176 | const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; | ||
149 | int ret; | 177 | int ret; |
150 | 178 | ||
151 | ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev, | 179 | ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev, |
@@ -153,9 +181,16 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) | |||
153 | if (ret < 0) | 181 | if (ret < 0) |
154 | return ret; | 182 | return ret; |
155 | 183 | ||
156 | reg = ioread32(wdt_base + WDT_MODE); | 184 | /* Set system reset function */ |
157 | reg |= (WDT_MODE_RST_EN | WDT_MODE_EN); | 185 | reg = readl(wdt_base + regs->wdt_cfg); |
158 | iowrite32(reg, wdt_base + WDT_MODE); | 186 | reg &= ~(regs->wdt_reset_mask); |
187 | reg |= ~(regs->wdt_reset_val); | ||
188 | writel(reg, wdt_base + regs->wdt_cfg); | ||
189 | |||
190 | /* Enable watchdog */ | ||
191 | reg = readl(wdt_base + regs->wdt_mode); | ||
192 | reg |= WDT_MODE_EN; | ||
193 | writel(reg, wdt_base + regs->wdt_mode); | ||
159 | 194 | ||
160 | return 0; | 195 | return 0; |
161 | } | 196 | } |
@@ -175,9 +210,35 @@ static const struct watchdog_ops sunxi_wdt_ops = { | |||
175 | .set_timeout = sunxi_wdt_set_timeout, | 210 | .set_timeout = sunxi_wdt_set_timeout, |
176 | }; | 211 | }; |
177 | 212 | ||
213 | static const struct sunxi_wdt_reg sun4i_wdt_reg = { | ||
214 | .wdt_ctrl = 0x00, | ||
215 | .wdt_cfg = 0x04, | ||
216 | .wdt_mode = 0x04, | ||
217 | .wdt_timeout_shift = 3, | ||
218 | .wdt_reset_mask = 0x02, | ||
219 | .wdt_reset_val = 0x02, | ||
220 | }; | ||
221 | |||
222 | static const struct sunxi_wdt_reg sun6i_wdt_reg = { | ||
223 | .wdt_ctrl = 0x10, | ||
224 | .wdt_cfg = 0x14, | ||
225 | .wdt_mode = 0x18, | ||
226 | .wdt_timeout_shift = 4, | ||
227 | .wdt_reset_mask = 0x03, | ||
228 | .wdt_reset_val = 0x01, | ||
229 | }; | ||
230 | |||
231 | static const struct of_device_id sunxi_wdt_dt_ids[] = { | ||
232 | { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg }, | ||
233 | { .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg }, | ||
234 | { /* sentinel */ } | ||
235 | }; | ||
236 | MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); | ||
237 | |||
178 | static int sunxi_wdt_probe(struct platform_device *pdev) | 238 | static int sunxi_wdt_probe(struct platform_device *pdev) |
179 | { | 239 | { |
180 | struct sunxi_wdt_dev *sunxi_wdt; | 240 | struct sunxi_wdt_dev *sunxi_wdt; |
241 | const struct of_device_id *device; | ||
181 | struct resource *res; | 242 | struct resource *res; |
182 | int err; | 243 | int err; |
183 | 244 | ||
@@ -187,6 +248,12 @@ static int sunxi_wdt_probe(struct platform_device *pdev) | |||
187 | 248 | ||
188 | platform_set_drvdata(pdev, sunxi_wdt); | 249 | platform_set_drvdata(pdev, sunxi_wdt); |
189 | 250 | ||
251 | device = of_match_device(sunxi_wdt_dt_ids, &pdev->dev); | ||
252 | if (!device) | ||
253 | return -ENODEV; | ||
254 | |||
255 | sunxi_wdt->wdt_regs = device->data; | ||
256 | |||
190 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 257 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
191 | sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); | 258 | sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); |
192 | if (IS_ERR(sunxi_wdt->wdt_base)) | 259 | if (IS_ERR(sunxi_wdt->wdt_base)) |
@@ -242,12 +309,6 @@ static void sunxi_wdt_shutdown(struct platform_device *pdev) | |||
242 | sunxi_wdt_stop(&sunxi_wdt->wdt_dev); | 309 | sunxi_wdt_stop(&sunxi_wdt->wdt_dev); |
243 | } | 310 | } |
244 | 311 | ||
245 | static const struct of_device_id sunxi_wdt_dt_ids[] = { | ||
246 | { .compatible = "allwinner,sun4i-a10-wdt" }, | ||
247 | { /* sentinel */ } | ||
248 | }; | ||
249 | MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); | ||
250 | |||
251 | static struct platform_driver sunxi_wdt_driver = { | 312 | static struct platform_driver sunxi_wdt_driver = { |
252 | .probe = sunxi_wdt_probe, | 313 | .probe = sunxi_wdt_probe, |
253 | .remove = sunxi_wdt_remove, | 314 | .remove = sunxi_wdt_remove, |
diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index afa9d6ef353a..dee9c6cbe6df 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c | |||
@@ -428,11 +428,7 @@ static int ts72xx_wdt_probe(struct platform_device *pdev) | |||
428 | 428 | ||
429 | static int ts72xx_wdt_remove(struct platform_device *pdev) | 429 | static int ts72xx_wdt_remove(struct platform_device *pdev) |
430 | { | 430 | { |
431 | int error; | 431 | return misc_deregister(&ts72xx_wdt_miscdev); |
432 | |||
433 | error = misc_deregister(&ts72xx_wdt_miscdev); | ||
434 | |||
435 | return error; | ||
436 | } | 432 | } |
437 | 433 | ||
438 | static struct platform_driver ts72xx_wdt_driver = { | 434 | static struct platform_driver ts72xx_wdt_driver = { |
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 1e0a317d3dcd..3860d02729dc 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c | |||
@@ -167,6 +167,9 @@ static struct page *balloon_next_page(struct page *page) | |||
167 | 167 | ||
168 | static enum bp_state update_schedule(enum bp_state state) | 168 | static enum bp_state update_schedule(enum bp_state state) |
169 | { | 169 | { |
170 | if (state == BP_ECANCELED) | ||
171 | return BP_ECANCELED; | ||
172 | |||
170 | if (state == BP_DONE) { | 173 | if (state == BP_DONE) { |
171 | balloon_stats.schedule_delay = 1; | 174 | balloon_stats.schedule_delay = 1; |
172 | balloon_stats.retry_count = 1; | 175 | balloon_stats.retry_count = 1; |
diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c index dd9c249ea311..95ee4302ffb8 100644 --- a/drivers/xen/pci.c +++ b/drivers/xen/pci.c | |||
@@ -41,24 +41,29 @@ static int xen_add_device(struct device *dev) | |||
41 | #endif | 41 | #endif |
42 | 42 | ||
43 | if (pci_seg_supported) { | 43 | if (pci_seg_supported) { |
44 | struct physdev_pci_device_add add = { | 44 | struct { |
45 | .seg = pci_domain_nr(pci_dev->bus), | 45 | struct physdev_pci_device_add add; |
46 | .bus = pci_dev->bus->number, | 46 | uint32_t pxm; |
47 | .devfn = pci_dev->devfn | 47 | } add_ext = { |
48 | .add.seg = pci_domain_nr(pci_dev->bus), | ||
49 | .add.bus = pci_dev->bus->number, | ||
50 | .add.devfn = pci_dev->devfn | ||
48 | }; | 51 | }; |
52 | struct physdev_pci_device_add *add = &add_ext.add; | ||
53 | |||
49 | #ifdef CONFIG_ACPI | 54 | #ifdef CONFIG_ACPI |
50 | acpi_handle handle; | 55 | acpi_handle handle; |
51 | #endif | 56 | #endif |
52 | 57 | ||
53 | #ifdef CONFIG_PCI_IOV | 58 | #ifdef CONFIG_PCI_IOV |
54 | if (pci_dev->is_virtfn) { | 59 | if (pci_dev->is_virtfn) { |
55 | add.flags = XEN_PCI_DEV_VIRTFN; | 60 | add->flags = XEN_PCI_DEV_VIRTFN; |
56 | add.physfn.bus = physfn->bus->number; | 61 | add->physfn.bus = physfn->bus->number; |
57 | add.physfn.devfn = physfn->devfn; | 62 | add->physfn.devfn = physfn->devfn; |
58 | } else | 63 | } else |
59 | #endif | 64 | #endif |
60 | if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) | 65 | if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) |
61 | add.flags = XEN_PCI_DEV_EXTFN; | 66 | add->flags = XEN_PCI_DEV_EXTFN; |
62 | 67 | ||
63 | #ifdef CONFIG_ACPI | 68 | #ifdef CONFIG_ACPI |
64 | handle = ACPI_HANDLE(&pci_dev->dev); | 69 | handle = ACPI_HANDLE(&pci_dev->dev); |
@@ -77,8 +82,8 @@ static int xen_add_device(struct device *dev) | |||
77 | status = acpi_evaluate_integer(handle, "_PXM", | 82 | status = acpi_evaluate_integer(handle, "_PXM", |
78 | NULL, &pxm); | 83 | NULL, &pxm); |
79 | if (ACPI_SUCCESS(status)) { | 84 | if (ACPI_SUCCESS(status)) { |
80 | add.optarr[0] = pxm; | 85 | add->optarr[0] = pxm; |
81 | add.flags |= XEN_PCI_DEV_PXM; | 86 | add->flags |= XEN_PCI_DEV_PXM; |
82 | break; | 87 | break; |
83 | } | 88 | } |
84 | status = acpi_get_parent(handle, &handle); | 89 | status = acpi_get_parent(handle, &handle); |
@@ -86,7 +91,7 @@ static int xen_add_device(struct device *dev) | |||
86 | } | 91 | } |
87 | #endif /* CONFIG_ACPI */ | 92 | #endif /* CONFIG_ACPI */ |
88 | 93 | ||
89 | r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, &add); | 94 | r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, add); |
90 | if (r != -ENOSYS) | 95 | if (r != -ENOSYS) |
91 | return r; | 96 | return r; |
92 | pci_seg_supported = false; | 97 | pci_seg_supported = false; |