diff options
-rw-r--r-- | arch/arm64/include/asm/acpi.h | 12 | ||||
-rw-r--r-- | arch/arm64/include/asm/fixmap.h | 7 | ||||
-rw-r--r-- | arch/arm64/mm/mmu.c | 4 | ||||
-rw-r--r-- | arch/x86/include/asm/fixmap.h | 6 | ||||
-rw-r--r-- | arch/x86/kernel/acpi/apei.c | 5 | ||||
-rw-r--r-- | drivers/acpi/Kconfig | 16 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 2 | ||||
-rw-r--r-- | drivers/acpi/apei/erst.c | 2 | ||||
-rw-r--r-- | drivers/acpi/apei/ghes.c | 117 | ||||
-rw-r--r-- | drivers/acpi/pmic/tps68470_pmic.c | 455 | ||||
-rw-r--r-- | drivers/acpi/x86/utils.c | 18 | ||||
-rw-r--r-- | include/acpi/apei.h | 1 |
12 files changed, 530 insertions, 115 deletions
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 59cca1d6ec54..32f465a80e4e 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h | |||
@@ -126,18 +126,6 @@ static inline const char *acpi_get_enable_method(int cpu) | |||
126 | */ | 126 | */ |
127 | #define acpi_disable_cmcff 1 | 127 | #define acpi_disable_cmcff 1 |
128 | pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr); | 128 | pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr); |
129 | |||
130 | /* | ||
131 | * Despite its name, this function must still broadcast the TLB | ||
132 | * invalidation in order to ensure other CPUs don't end up with junk | ||
133 | * entries as a result of speculation. Unusually, its also called in | ||
134 | * IRQ context (ghes_iounmap_irq) so if we ever need to use IPIs for | ||
135 | * TLB broadcasting, then we're in trouble here. | ||
136 | */ | ||
137 | static inline void arch_apei_flush_tlb_one(unsigned long addr) | ||
138 | { | ||
139 | flush_tlb_kernel_range(addr, addr + PAGE_SIZE); | ||
140 | } | ||
141 | #endif /* CONFIG_ACPI_APEI */ | 129 | #endif /* CONFIG_ACPI_APEI */ |
142 | 130 | ||
143 | #ifdef CONFIG_ACPI_NUMA | 131 | #ifdef CONFIG_ACPI_NUMA |
diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h index caf86be815ba..4052ec39e8db 100644 --- a/arch/arm64/include/asm/fixmap.h +++ b/arch/arm64/include/asm/fixmap.h | |||
@@ -51,6 +51,13 @@ enum fixed_addresses { | |||
51 | 51 | ||
52 | FIX_EARLYCON_MEM_BASE, | 52 | FIX_EARLYCON_MEM_BASE, |
53 | FIX_TEXT_POKE0, | 53 | FIX_TEXT_POKE0, |
54 | |||
55 | #ifdef CONFIG_ACPI_APEI_GHES | ||
56 | /* Used for GHES mapping from assorted contexts */ | ||
57 | FIX_APEI_GHES_IRQ, | ||
58 | FIX_APEI_GHES_NMI, | ||
59 | #endif /* CONFIG_ACPI_APEI_GHES */ | ||
60 | |||
54 | __end_of_permanent_fixed_addresses, | 61 | __end_of_permanent_fixed_addresses, |
55 | 62 | ||
56 | /* | 63 | /* |
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index f1eb15e0e864..267d2b79d52d 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c | |||
@@ -778,6 +778,10 @@ void __init early_fixmap_init(void) | |||
778 | } | 778 | } |
779 | } | 779 | } |
780 | 780 | ||
781 | /* | ||
782 | * Unusually, this is also called in IRQ context (ghes_iounmap_irq) so if we | ||
783 | * ever need to use IPIs for TLB broadcasting, then we're in trouble here. | ||
784 | */ | ||
781 | void __set_fixmap(enum fixed_addresses idx, | 785 | void __set_fixmap(enum fixed_addresses idx, |
782 | phys_addr_t phys, pgprot_t flags) | 786 | phys_addr_t phys, pgprot_t flags) |
783 | { | 787 | { |
diff --git a/arch/x86/include/asm/fixmap.h b/arch/x86/include/asm/fixmap.h index dcd9fb55e679..b0c505fe9a95 100644 --- a/arch/x86/include/asm/fixmap.h +++ b/arch/x86/include/asm/fixmap.h | |||
@@ -104,6 +104,12 @@ enum fixed_addresses { | |||
104 | FIX_GDT_REMAP_BEGIN, | 104 | FIX_GDT_REMAP_BEGIN, |
105 | FIX_GDT_REMAP_END = FIX_GDT_REMAP_BEGIN + NR_CPUS - 1, | 105 | FIX_GDT_REMAP_END = FIX_GDT_REMAP_BEGIN + NR_CPUS - 1, |
106 | 106 | ||
107 | #ifdef CONFIG_ACPI_APEI_GHES | ||
108 | /* Used for GHES mapping from assorted contexts */ | ||
109 | FIX_APEI_GHES_IRQ, | ||
110 | FIX_APEI_GHES_NMI, | ||
111 | #endif | ||
112 | |||
107 | __end_of_permanent_fixed_addresses, | 113 | __end_of_permanent_fixed_addresses, |
108 | 114 | ||
109 | /* | 115 | /* |
diff --git a/arch/x86/kernel/acpi/apei.c b/arch/x86/kernel/acpi/apei.c index ea3046e0b0cf..bb8d300fecbd 100644 --- a/arch/x86/kernel/acpi/apei.c +++ b/arch/x86/kernel/acpi/apei.c | |||
@@ -52,8 +52,3 @@ void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) | |||
52 | apei_mce_report_mem_error(sev, mem_err); | 52 | apei_mce_report_mem_error(sev, mem_err); |
53 | #endif | 53 | #endif |
54 | } | 54 | } |
55 | |||
56 | void arch_apei_flush_tlb_one(unsigned long addr) | ||
57 | { | ||
58 | __flush_tlb_one(addr); | ||
59 | } | ||
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5b1938f4b626..c9781175e59e 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -536,4 +536,20 @@ if ARM64 | |||
536 | source "drivers/acpi/arm64/Kconfig" | 536 | source "drivers/acpi/arm64/Kconfig" |
537 | endif | 537 | endif |
538 | 538 | ||
539 | config TPS68470_PMIC_OPREGION | ||
540 | bool "ACPI operation region support for TPS68470 PMIC" | ||
541 | depends on MFD_TPS68470 | ||
542 | help | ||
543 | This config adds ACPI operation region support for TI TPS68470 PMIC. | ||
544 | TPS68470 device is an advanced power management unit that powers | ||
545 | a Compact Camera Module (CCM), generates clocks for image sensors, | ||
546 | drives a dual LED for flash and incorporates two LED drivers for | ||
547 | general purpose indicators. | ||
548 | This driver enables ACPI operation region support control voltage | ||
549 | regulators and clocks. | ||
550 | |||
551 | This option is a bool as it provides an ACPI operation | ||
552 | region, which must be available before any of the devices | ||
553 | using this, are probed. | ||
554 | |||
539 | endif # ACPI | 555 | endif # ACPI |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index cd1abc9bc325..709c1120f315 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -108,6 +108,8 @@ obj-$(CONFIG_CHT_WC_PMIC_OPREGION) += pmic/intel_pmic_chtwc.o | |||
108 | 108 | ||
109 | obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o | 109 | obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o |
110 | 110 | ||
111 | obj-$(CONFIG_TPS68470_PMIC_OPREGION) += pmic/tps68470_pmic.o | ||
112 | |||
111 | video-objs += acpi_video.o video_detect.o | 113 | video-objs += acpi_video.o video_detect.o |
112 | obj-y += dptf/ | 114 | obj-y += dptf/ |
113 | 115 | ||
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 2c462beee551..6742f6c68034 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c | |||
@@ -1061,7 +1061,7 @@ static int erst_writer(struct pstore_record *record) | |||
1061 | rcd->hdr.error_severity = CPER_SEV_FATAL; | 1061 | rcd->hdr.error_severity = CPER_SEV_FATAL; |
1062 | /* timestamp valid. platform_id, partition_id are invalid */ | 1062 | /* timestamp valid. platform_id, partition_id are invalid */ |
1063 | rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; | 1063 | rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; |
1064 | rcd->hdr.timestamp = get_seconds(); | 1064 | rcd->hdr.timestamp = ktime_get_real_seconds(); |
1065 | rcd->hdr.record_length = sizeof(*rcd) + record->size; | 1065 | rcd->hdr.record_length = sizeof(*rcd) + record->size; |
1066 | rcd->hdr.creator_id = CPER_CREATOR_PSTORE; | 1066 | rcd->hdr.creator_id = CPER_CREATOR_PSTORE; |
1067 | rcd->hdr.notification_type = CPER_NOTIFY_MCE; | 1067 | rcd->hdr.notification_type = CPER_NOTIFY_MCE; |
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3c3a37b8503b..f14695e744d0 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c | |||
@@ -51,6 +51,7 @@ | |||
51 | #include <acpi/actbl1.h> | 51 | #include <acpi/actbl1.h> |
52 | #include <acpi/ghes.h> | 52 | #include <acpi/ghes.h> |
53 | #include <acpi/apei.h> | 53 | #include <acpi/apei.h> |
54 | #include <asm/fixmap.h> | ||
54 | #include <asm/tlbflush.h> | 55 | #include <asm/tlbflush.h> |
55 | #include <ras/ras_event.h> | 56 | #include <ras/ras_event.h> |
56 | 57 | ||
@@ -112,22 +113,10 @@ static DEFINE_MUTEX(ghes_list_mutex); | |||
112 | * Because the memory area used to transfer hardware error information | 113 | * Because the memory area used to transfer hardware error information |
113 | * from BIOS to Linux can be determined only in NMI, IRQ or timer | 114 | * from BIOS to Linux can be determined only in NMI, IRQ or timer |
114 | * handler, but general ioremap can not be used in atomic context, so | 115 | * handler, but general ioremap can not be used in atomic context, so |
115 | * a special version of atomic ioremap is implemented for that. | 116 | * the fixmap is used instead. |
116 | */ | 117 | * |
117 | 118 | * These 2 spinlocks are used to prevent the fixmap entries from being used | |
118 | /* | 119 | * simultaneously. |
119 | * Two virtual pages are used, one for IRQ/PROCESS context, the other for | ||
120 | * NMI context (optionally). | ||
121 | */ | ||
122 | #define GHES_IOREMAP_PAGES 2 | ||
123 | #define GHES_IOREMAP_IRQ_PAGE(base) (base) | ||
124 | #define GHES_IOREMAP_NMI_PAGE(base) ((base) + PAGE_SIZE) | ||
125 | |||
126 | /* virtual memory area for atomic ioremap */ | ||
127 | static struct vm_struct *ghes_ioremap_area; | ||
128 | /* | ||
129 | * These 2 spinlock is used to prevent atomic ioremap virtual memory | ||
130 | * area from being mapped simultaneously. | ||
131 | */ | 120 | */ |
132 | static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); | 121 | static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); |
133 | static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); | 122 | static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); |
@@ -140,71 +129,38 @@ static atomic_t ghes_estatus_cache_alloced; | |||
140 | 129 | ||
141 | static int ghes_panic_timeout __read_mostly = 30; | 130 | static int ghes_panic_timeout __read_mostly = 30; |
142 | 131 | ||
143 | static int ghes_ioremap_init(void) | ||
144 | { | ||
145 | ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES, | ||
146 | VM_IOREMAP, VMALLOC_START, VMALLOC_END); | ||
147 | if (!ghes_ioremap_area) { | ||
148 | pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n"); | ||
149 | return -ENOMEM; | ||
150 | } | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static void ghes_ioremap_exit(void) | ||
156 | { | ||
157 | free_vm_area(ghes_ioremap_area); | ||
158 | } | ||
159 | |||
160 | static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) | 132 | static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) |
161 | { | 133 | { |
162 | unsigned long vaddr; | ||
163 | phys_addr_t paddr; | 134 | phys_addr_t paddr; |
164 | pgprot_t prot; | 135 | pgprot_t prot; |
165 | 136 | ||
166 | vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr); | ||
167 | |||
168 | paddr = pfn << PAGE_SHIFT; | 137 | paddr = pfn << PAGE_SHIFT; |
169 | prot = arch_apei_get_mem_attribute(paddr); | 138 | prot = arch_apei_get_mem_attribute(paddr); |
170 | ioremap_page_range(vaddr, vaddr + PAGE_SIZE, paddr, prot); | 139 | __set_fixmap(FIX_APEI_GHES_NMI, paddr, prot); |
171 | 140 | ||
172 | return (void __iomem *)vaddr; | 141 | return (void __iomem *) fix_to_virt(FIX_APEI_GHES_NMI); |
173 | } | 142 | } |
174 | 143 | ||
175 | static void __iomem *ghes_ioremap_pfn_irq(u64 pfn) | 144 | static void __iomem *ghes_ioremap_pfn_irq(u64 pfn) |
176 | { | 145 | { |
177 | unsigned long vaddr, paddr; | 146 | phys_addr_t paddr; |
178 | pgprot_t prot; | 147 | pgprot_t prot; |
179 | 148 | ||
180 | vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr); | ||
181 | |||
182 | paddr = pfn << PAGE_SHIFT; | 149 | paddr = pfn << PAGE_SHIFT; |
183 | prot = arch_apei_get_mem_attribute(paddr); | 150 | prot = arch_apei_get_mem_attribute(paddr); |
151 | __set_fixmap(FIX_APEI_GHES_IRQ, paddr, prot); | ||
184 | 152 | ||
185 | ioremap_page_range(vaddr, vaddr + PAGE_SIZE, paddr, prot); | 153 | return (void __iomem *) fix_to_virt(FIX_APEI_GHES_IRQ); |
186 | |||
187 | return (void __iomem *)vaddr; | ||
188 | } | 154 | } |
189 | 155 | ||
190 | static void ghes_iounmap_nmi(void __iomem *vaddr_ptr) | 156 | static void ghes_iounmap_nmi(void) |
191 | { | 157 | { |
192 | unsigned long vaddr = (unsigned long __force)vaddr_ptr; | 158 | clear_fixmap(FIX_APEI_GHES_NMI); |
193 | void *base = ghes_ioremap_area->addr; | ||
194 | |||
195 | BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); | ||
196 | unmap_kernel_range_noflush(vaddr, PAGE_SIZE); | ||
197 | arch_apei_flush_tlb_one(vaddr); | ||
198 | } | 159 | } |
199 | 160 | ||
200 | static void ghes_iounmap_irq(void __iomem *vaddr_ptr) | 161 | static void ghes_iounmap_irq(void) |
201 | { | 162 | { |
202 | unsigned long vaddr = (unsigned long __force)vaddr_ptr; | 163 | clear_fixmap(FIX_APEI_GHES_IRQ); |
203 | void *base = ghes_ioremap_area->addr; | ||
204 | |||
205 | BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); | ||
206 | unmap_kernel_range_noflush(vaddr, PAGE_SIZE); | ||
207 | arch_apei_flush_tlb_one(vaddr); | ||
208 | } | 164 | } |
209 | 165 | ||
210 | static int ghes_estatus_pool_init(void) | 166 | static int ghes_estatus_pool_init(void) |
@@ -360,10 +316,10 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, | |||
360 | paddr += trunk; | 316 | paddr += trunk; |
361 | buffer += trunk; | 317 | buffer += trunk; |
362 | if (in_nmi) { | 318 | if (in_nmi) { |
363 | ghes_iounmap_nmi(vaddr); | 319 | ghes_iounmap_nmi(); |
364 | raw_spin_unlock(&ghes_ioremap_lock_nmi); | 320 | raw_spin_unlock(&ghes_ioremap_lock_nmi); |
365 | } else { | 321 | } else { |
366 | ghes_iounmap_irq(vaddr); | 322 | ghes_iounmap_irq(); |
367 | spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); | 323 | spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); |
368 | } | 324 | } |
369 | } | 325 | } |
@@ -851,17 +807,8 @@ static void ghes_sea_remove(struct ghes *ghes) | |||
851 | synchronize_rcu(); | 807 | synchronize_rcu(); |
852 | } | 808 | } |
853 | #else /* CONFIG_ACPI_APEI_SEA */ | 809 | #else /* CONFIG_ACPI_APEI_SEA */ |
854 | static inline void ghes_sea_add(struct ghes *ghes) | 810 | static inline void ghes_sea_add(struct ghes *ghes) { } |
855 | { | 811 | static inline void ghes_sea_remove(struct ghes *ghes) { } |
856 | pr_err(GHES_PFX "ID: %d, trying to add SEA notification which is not supported\n", | ||
857 | ghes->generic->header.source_id); | ||
858 | } | ||
859 | |||
860 | static inline void ghes_sea_remove(struct ghes *ghes) | ||
861 | { | ||
862 | pr_err(GHES_PFX "ID: %d, trying to remove SEA notification which is not supported\n", | ||
863 | ghes->generic->header.source_id); | ||
864 | } | ||
865 | #endif /* CONFIG_ACPI_APEI_SEA */ | 812 | #endif /* CONFIG_ACPI_APEI_SEA */ |
866 | 813 | ||
867 | #ifdef CONFIG_HAVE_ACPI_APEI_NMI | 814 | #ifdef CONFIG_HAVE_ACPI_APEI_NMI |
@@ -1063,23 +1010,9 @@ static void ghes_nmi_init_cxt(void) | |||
1063 | init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); | 1010 | init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); |
1064 | } | 1011 | } |
1065 | #else /* CONFIG_HAVE_ACPI_APEI_NMI */ | 1012 | #else /* CONFIG_HAVE_ACPI_APEI_NMI */ |
1066 | static inline void ghes_nmi_add(struct ghes *ghes) | 1013 | static inline void ghes_nmi_add(struct ghes *ghes) { } |
1067 | { | 1014 | static inline void ghes_nmi_remove(struct ghes *ghes) { } |
1068 | pr_err(GHES_PFX "ID: %d, trying to add NMI notification which is not supported!\n", | 1015 | static inline void ghes_nmi_init_cxt(void) { } |
1069 | ghes->generic->header.source_id); | ||
1070 | BUG(); | ||
1071 | } | ||
1072 | |||
1073 | static inline void ghes_nmi_remove(struct ghes *ghes) | ||
1074 | { | ||
1075 | pr_err(GHES_PFX "ID: %d, trying to remove NMI notification which is not supported!\n", | ||
1076 | ghes->generic->header.source_id); | ||
1077 | BUG(); | ||
1078 | } | ||
1079 | |||
1080 | static inline void ghes_nmi_init_cxt(void) | ||
1081 | { | ||
1082 | } | ||
1083 | #endif /* CONFIG_HAVE_ACPI_APEI_NMI */ | 1016 | #endif /* CONFIG_HAVE_ACPI_APEI_NMI */ |
1084 | 1017 | ||
1085 | static int ghes_probe(struct platform_device *ghes_dev) | 1018 | static int ghes_probe(struct platform_device *ghes_dev) |
@@ -1285,13 +1218,9 @@ static int __init ghes_init(void) | |||
1285 | 1218 | ||
1286 | ghes_nmi_init_cxt(); | 1219 | ghes_nmi_init_cxt(); |
1287 | 1220 | ||
1288 | rc = ghes_ioremap_init(); | ||
1289 | if (rc) | ||
1290 | goto err; | ||
1291 | |||
1292 | rc = ghes_estatus_pool_init(); | 1221 | rc = ghes_estatus_pool_init(); |
1293 | if (rc) | 1222 | if (rc) |
1294 | goto err_ioremap_exit; | 1223 | goto err; |
1295 | 1224 | ||
1296 | rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE * | 1225 | rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE * |
1297 | GHES_ESTATUS_CACHE_ALLOCED_MAX); | 1226 | GHES_ESTATUS_CACHE_ALLOCED_MAX); |
@@ -1315,8 +1244,6 @@ static int __init ghes_init(void) | |||
1315 | return 0; | 1244 | return 0; |
1316 | err_pool_exit: | 1245 | err_pool_exit: |
1317 | ghes_estatus_pool_exit(); | 1246 | ghes_estatus_pool_exit(); |
1318 | err_ioremap_exit: | ||
1319 | ghes_ioremap_exit(); | ||
1320 | err: | 1247 | err: |
1321 | return rc; | 1248 | return rc; |
1322 | } | 1249 | } |
diff --git a/drivers/acpi/pmic/tps68470_pmic.c b/drivers/acpi/pmic/tps68470_pmic.c new file mode 100644 index 000000000000..7f3c567e8168 --- /dev/null +++ b/drivers/acpi/pmic/tps68470_pmic.c | |||
@@ -0,0 +1,455 @@ | |||
1 | /* | ||
2 | * TI TPS68470 PMIC operation region driver | ||
3 | * | ||
4 | * Copyright (C) 2017 Intel Corporation. All rights reserved. | ||
5 | * | ||
6 | * Author: Rajmohan Mani <rajmohan.mani@intel.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License version | ||
10 | * 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
13 | * kind, whether express or implied; without even the implied warranty | ||
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * Based on drivers/acpi/pmic/intel_pmic* drivers | ||
18 | */ | ||
19 | |||
20 | #include <linux/acpi.h> | ||
21 | #include <linux/mfd/tps68470.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/regmap.h> | ||
25 | |||
26 | struct tps68470_pmic_table { | ||
27 | u32 address; /* operation region address */ | ||
28 | u32 reg; /* corresponding register */ | ||
29 | u32 bitmask; /* bit mask for power, clock */ | ||
30 | }; | ||
31 | |||
32 | #define TI_PMIC_POWER_OPREGION_ID 0xB0 | ||
33 | #define TI_PMIC_VR_VAL_OPREGION_ID 0xB1 | ||
34 | #define TI_PMIC_CLOCK_OPREGION_ID 0xB2 | ||
35 | #define TI_PMIC_CLKFREQ_OPREGION_ID 0xB3 | ||
36 | |||
37 | struct tps68470_pmic_opregion { | ||
38 | struct mutex lock; | ||
39 | struct regmap *regmap; | ||
40 | }; | ||
41 | |||
42 | #define S_IO_I2C_EN (BIT(0) | BIT(1)) | ||
43 | |||
44 | static const struct tps68470_pmic_table power_table[] = { | ||
45 | { | ||
46 | .address = 0x00, | ||
47 | .reg = TPS68470_REG_S_I2C_CTL, | ||
48 | .bitmask = S_IO_I2C_EN, | ||
49 | /* S_I2C_CTL */ | ||
50 | }, | ||
51 | { | ||
52 | .address = 0x04, | ||
53 | .reg = TPS68470_REG_VCMCTL, | ||
54 | .bitmask = BIT(0), | ||
55 | /* VCMCTL */ | ||
56 | }, | ||
57 | { | ||
58 | .address = 0x08, | ||
59 | .reg = TPS68470_REG_VAUX1CTL, | ||
60 | .bitmask = BIT(0), | ||
61 | /* VAUX1_CTL */ | ||
62 | }, | ||
63 | { | ||
64 | .address = 0x0C, | ||
65 | .reg = TPS68470_REG_VAUX2CTL, | ||
66 | .bitmask = BIT(0), | ||
67 | /* VAUX2CTL */ | ||
68 | }, | ||
69 | { | ||
70 | .address = 0x10, | ||
71 | .reg = TPS68470_REG_VACTL, | ||
72 | .bitmask = BIT(0), | ||
73 | /* VACTL */ | ||
74 | }, | ||
75 | { | ||
76 | .address = 0x14, | ||
77 | .reg = TPS68470_REG_VDCTL, | ||
78 | .bitmask = BIT(0), | ||
79 | /* VDCTL */ | ||
80 | }, | ||
81 | }; | ||
82 | |||
83 | /* Table to set voltage regulator value */ | ||
84 | static const struct tps68470_pmic_table vr_val_table[] = { | ||
85 | { | ||
86 | .address = 0x00, | ||
87 | .reg = TPS68470_REG_VSIOVAL, | ||
88 | .bitmask = TPS68470_VSIOVAL_IOVOLT_MASK, | ||
89 | /* TPS68470_REG_VSIOVAL */ | ||
90 | }, | ||
91 | { | ||
92 | .address = 0x04, | ||
93 | .reg = TPS68470_REG_VIOVAL, | ||
94 | .bitmask = TPS68470_VIOVAL_IOVOLT_MASK, | ||
95 | /* TPS68470_REG_VIOVAL */ | ||
96 | }, | ||
97 | { | ||
98 | .address = 0x08, | ||
99 | .reg = TPS68470_REG_VCMVAL, | ||
100 | .bitmask = TPS68470_VCMVAL_VCVOLT_MASK, | ||
101 | /* TPS68470_REG_VCMVAL */ | ||
102 | }, | ||
103 | { | ||
104 | .address = 0x0C, | ||
105 | .reg = TPS68470_REG_VAUX1VAL, | ||
106 | .bitmask = TPS68470_VAUX1VAL_AUX1VOLT_MASK, | ||
107 | /* TPS68470_REG_VAUX1VAL */ | ||
108 | }, | ||
109 | { | ||
110 | .address = 0x10, | ||
111 | .reg = TPS68470_REG_VAUX2VAL, | ||
112 | .bitmask = TPS68470_VAUX2VAL_AUX2VOLT_MASK, | ||
113 | /* TPS68470_REG_VAUX2VAL */ | ||
114 | }, | ||
115 | { | ||
116 | .address = 0x14, | ||
117 | .reg = TPS68470_REG_VAVAL, | ||
118 | .bitmask = TPS68470_VAVAL_AVOLT_MASK, | ||
119 | /* TPS68470_REG_VAVAL */ | ||
120 | }, | ||
121 | { | ||
122 | .address = 0x18, | ||
123 | .reg = TPS68470_REG_VDVAL, | ||
124 | .bitmask = TPS68470_VDVAL_DVOLT_MASK, | ||
125 | /* TPS68470_REG_VDVAL */ | ||
126 | }, | ||
127 | }; | ||
128 | |||
129 | /* Table to configure clock frequency */ | ||
130 | static const struct tps68470_pmic_table clk_freq_table[] = { | ||
131 | { | ||
132 | .address = 0x00, | ||
133 | .reg = TPS68470_REG_POSTDIV2, | ||
134 | .bitmask = BIT(0) | BIT(1), | ||
135 | /* TPS68470_REG_POSTDIV2 */ | ||
136 | }, | ||
137 | { | ||
138 | .address = 0x04, | ||
139 | .reg = TPS68470_REG_BOOSTDIV, | ||
140 | .bitmask = 0x1F, | ||
141 | /* TPS68470_REG_BOOSTDIV */ | ||
142 | }, | ||
143 | { | ||
144 | .address = 0x08, | ||
145 | .reg = TPS68470_REG_BUCKDIV, | ||
146 | .bitmask = 0x0F, | ||
147 | /* TPS68470_REG_BUCKDIV */ | ||
148 | }, | ||
149 | { | ||
150 | .address = 0x0C, | ||
151 | .reg = TPS68470_REG_PLLSWR, | ||
152 | .bitmask = 0x13, | ||
153 | /* TPS68470_REG_PLLSWR */ | ||
154 | }, | ||
155 | { | ||
156 | .address = 0x10, | ||
157 | .reg = TPS68470_REG_XTALDIV, | ||
158 | .bitmask = 0xFF, | ||
159 | /* TPS68470_REG_XTALDIV */ | ||
160 | }, | ||
161 | { | ||
162 | .address = 0x14, | ||
163 | .reg = TPS68470_REG_PLLDIV, | ||
164 | .bitmask = 0xFF, | ||
165 | /* TPS68470_REG_PLLDIV */ | ||
166 | }, | ||
167 | { | ||
168 | .address = 0x18, | ||
169 | .reg = TPS68470_REG_POSTDIV, | ||
170 | .bitmask = 0x83, | ||
171 | /* TPS68470_REG_POSTDIV */ | ||
172 | }, | ||
173 | }; | ||
174 | |||
175 | /* Table to configure and enable clocks */ | ||
176 | static const struct tps68470_pmic_table clk_table[] = { | ||
177 | { | ||
178 | .address = 0x00, | ||
179 | .reg = TPS68470_REG_PLLCTL, | ||
180 | .bitmask = 0xF5, | ||
181 | /* TPS68470_REG_PLLCTL */ | ||
182 | }, | ||
183 | { | ||
184 | .address = 0x04, | ||
185 | .reg = TPS68470_REG_PLLCTL2, | ||
186 | .bitmask = BIT(0), | ||
187 | /* TPS68470_REG_PLLCTL2 */ | ||
188 | }, | ||
189 | { | ||
190 | .address = 0x08, | ||
191 | .reg = TPS68470_REG_CLKCFG1, | ||
192 | .bitmask = TPS68470_CLKCFG1_MODE_A_MASK | | ||
193 | TPS68470_CLKCFG1_MODE_B_MASK, | ||
194 | /* TPS68470_REG_CLKCFG1 */ | ||
195 | }, | ||
196 | { | ||
197 | .address = 0x0C, | ||
198 | .reg = TPS68470_REG_CLKCFG2, | ||
199 | .bitmask = TPS68470_CLKCFG1_MODE_A_MASK | | ||
200 | TPS68470_CLKCFG1_MODE_B_MASK, | ||
201 | /* TPS68470_REG_CLKCFG2 */ | ||
202 | }, | ||
203 | }; | ||
204 | |||
205 | static int pmic_get_reg_bit(u64 address, | ||
206 | const struct tps68470_pmic_table *table, | ||
207 | const unsigned int table_size, int *reg, | ||
208 | int *bitmask) | ||
209 | { | ||
210 | u64 i; | ||
211 | |||
212 | i = address / 4; | ||
213 | if (i >= table_size) | ||
214 | return -ENOENT; | ||
215 | |||
216 | if (!reg || !bitmask) | ||
217 | return -EINVAL; | ||
218 | |||
219 | *reg = table[i].reg; | ||
220 | *bitmask = table[i].bitmask; | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int tps68470_pmic_get_power(struct regmap *regmap, int reg, | ||
226 | int bitmask, u64 *value) | ||
227 | { | ||
228 | unsigned int data; | ||
229 | |||
230 | if (regmap_read(regmap, reg, &data)) | ||
231 | return -EIO; | ||
232 | |||
233 | *value = (data & bitmask) ? 1 : 0; | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static int tps68470_pmic_get_vr_val(struct regmap *regmap, int reg, | ||
238 | int bitmask, u64 *value) | ||
239 | { | ||
240 | unsigned int data; | ||
241 | |||
242 | if (regmap_read(regmap, reg, &data)) | ||
243 | return -EIO; | ||
244 | |||
245 | *value = data & bitmask; | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int tps68470_pmic_get_clk(struct regmap *regmap, int reg, | ||
250 | int bitmask, u64 *value) | ||
251 | { | ||
252 | unsigned int data; | ||
253 | |||
254 | if (regmap_read(regmap, reg, &data)) | ||
255 | return -EIO; | ||
256 | |||
257 | *value = (data & bitmask) ? 1 : 0; | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int tps68470_pmic_get_clk_freq(struct regmap *regmap, int reg, | ||
262 | int bitmask, u64 *value) | ||
263 | { | ||
264 | unsigned int data; | ||
265 | |||
266 | if (regmap_read(regmap, reg, &data)) | ||
267 | return -EIO; | ||
268 | |||
269 | *value = data & bitmask; | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int ti_tps68470_regmap_update_bits(struct regmap *regmap, int reg, | ||
274 | int bitmask, u64 value) | ||
275 | { | ||
276 | return regmap_update_bits(regmap, reg, bitmask, value); | ||
277 | } | ||
278 | |||
279 | static acpi_status tps68470_pmic_common_handler(u32 function, | ||
280 | acpi_physical_address address, | ||
281 | u32 bits, u64 *value, | ||
282 | void *region_context, | ||
283 | int (*get)(struct regmap *, | ||
284 | int, int, u64 *), | ||
285 | int (*update)(struct regmap *, | ||
286 | int, int, u64), | ||
287 | const struct tps68470_pmic_table *tbl, | ||
288 | unsigned int tbl_size) | ||
289 | { | ||
290 | struct tps68470_pmic_opregion *opregion = region_context; | ||
291 | struct regmap *regmap = opregion->regmap; | ||
292 | int reg, ret, bitmask; | ||
293 | |||
294 | if (bits != 32) | ||
295 | return AE_BAD_PARAMETER; | ||
296 | |||
297 | ret = pmic_get_reg_bit(address, tbl, tbl_size, ®, &bitmask); | ||
298 | if (ret < 0) | ||
299 | return AE_BAD_PARAMETER; | ||
300 | |||
301 | if (function == ACPI_WRITE && *value > bitmask) | ||
302 | return AE_BAD_PARAMETER; | ||
303 | |||
304 | mutex_lock(&opregion->lock); | ||
305 | |||
306 | ret = (function == ACPI_READ) ? | ||
307 | get(regmap, reg, bitmask, value) : | ||
308 | update(regmap, reg, bitmask, *value); | ||
309 | |||
310 | mutex_unlock(&opregion->lock); | ||
311 | |||
312 | return ret ? AE_ERROR : AE_OK; | ||
313 | } | ||
314 | |||
315 | static acpi_status tps68470_pmic_cfreq_handler(u32 function, | ||
316 | acpi_physical_address address, | ||
317 | u32 bits, u64 *value, | ||
318 | void *handler_context, | ||
319 | void *region_context) | ||
320 | { | ||
321 | return tps68470_pmic_common_handler(function, address, bits, value, | ||
322 | region_context, | ||
323 | tps68470_pmic_get_clk_freq, | ||
324 | ti_tps68470_regmap_update_bits, | ||
325 | clk_freq_table, | ||
326 | ARRAY_SIZE(clk_freq_table)); | ||
327 | } | ||
328 | |||
329 | static acpi_status tps68470_pmic_clk_handler(u32 function, | ||
330 | acpi_physical_address address, u32 bits, | ||
331 | u64 *value, void *handler_context, | ||
332 | void *region_context) | ||
333 | { | ||
334 | return tps68470_pmic_common_handler(function, address, bits, value, | ||
335 | region_context, | ||
336 | tps68470_pmic_get_clk, | ||
337 | ti_tps68470_regmap_update_bits, | ||
338 | clk_table, | ||
339 | ARRAY_SIZE(clk_table)); | ||
340 | } | ||
341 | |||
342 | static acpi_status tps68470_pmic_vrval_handler(u32 function, | ||
343 | acpi_physical_address address, | ||
344 | u32 bits, u64 *value, | ||
345 | void *handler_context, | ||
346 | void *region_context) | ||
347 | { | ||
348 | return tps68470_pmic_common_handler(function, address, bits, value, | ||
349 | region_context, | ||
350 | tps68470_pmic_get_vr_val, | ||
351 | ti_tps68470_regmap_update_bits, | ||
352 | vr_val_table, | ||
353 | ARRAY_SIZE(vr_val_table)); | ||
354 | } | ||
355 | |||
356 | static acpi_status tps68470_pmic_pwr_handler(u32 function, | ||
357 | acpi_physical_address address, | ||
358 | u32 bits, u64 *value, | ||
359 | void *handler_context, | ||
360 | void *region_context) | ||
361 | { | ||
362 | if (bits != 32) | ||
363 | return AE_BAD_PARAMETER; | ||
364 | |||
365 | /* set/clear for bit 0, bits 0 and 1 together */ | ||
366 | if (function == ACPI_WRITE && | ||
367 | !(*value == 0 || *value == 1 || *value == 3)) { | ||
368 | return AE_BAD_PARAMETER; | ||
369 | } | ||
370 | |||
371 | return tps68470_pmic_common_handler(function, address, bits, value, | ||
372 | region_context, | ||
373 | tps68470_pmic_get_power, | ||
374 | ti_tps68470_regmap_update_bits, | ||
375 | power_table, | ||
376 | ARRAY_SIZE(power_table)); | ||
377 | } | ||
378 | |||
379 | static int tps68470_pmic_opregion_probe(struct platform_device *pdev) | ||
380 | { | ||
381 | struct regmap *tps68470_regmap = dev_get_drvdata(pdev->dev.parent); | ||
382 | acpi_handle handle = ACPI_HANDLE(pdev->dev.parent); | ||
383 | struct device *dev = &pdev->dev; | ||
384 | struct tps68470_pmic_opregion *opregion; | ||
385 | acpi_status status; | ||
386 | |||
387 | if (!dev || !tps68470_regmap) { | ||
388 | dev_warn(dev, "dev or regmap is NULL\n"); | ||
389 | return -EINVAL; | ||
390 | } | ||
391 | |||
392 | if (!handle) { | ||
393 | dev_warn(dev, "acpi handle is NULL\n"); | ||
394 | return -ENODEV; | ||
395 | } | ||
396 | |||
397 | opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL); | ||
398 | if (!opregion) | ||
399 | return -ENOMEM; | ||
400 | |||
401 | mutex_init(&opregion->lock); | ||
402 | opregion->regmap = tps68470_regmap; | ||
403 | |||
404 | status = acpi_install_address_space_handler(handle, | ||
405 | TI_PMIC_POWER_OPREGION_ID, | ||
406 | tps68470_pmic_pwr_handler, | ||
407 | NULL, opregion); | ||
408 | if (ACPI_FAILURE(status)) | ||
409 | goto out_mutex_destroy; | ||
410 | |||
411 | status = acpi_install_address_space_handler(handle, | ||
412 | TI_PMIC_VR_VAL_OPREGION_ID, | ||
413 | tps68470_pmic_vrval_handler, | ||
414 | NULL, opregion); | ||
415 | if (ACPI_FAILURE(status)) | ||
416 | goto out_remove_power_handler; | ||
417 | |||
418 | status = acpi_install_address_space_handler(handle, | ||
419 | TI_PMIC_CLOCK_OPREGION_ID, | ||
420 | tps68470_pmic_clk_handler, | ||
421 | NULL, opregion); | ||
422 | if (ACPI_FAILURE(status)) | ||
423 | goto out_remove_vr_val_handler; | ||
424 | |||
425 | status = acpi_install_address_space_handler(handle, | ||
426 | TI_PMIC_CLKFREQ_OPREGION_ID, | ||
427 | tps68470_pmic_cfreq_handler, | ||
428 | NULL, opregion); | ||
429 | if (ACPI_FAILURE(status)) | ||
430 | goto out_remove_clk_handler; | ||
431 | |||
432 | return 0; | ||
433 | |||
434 | out_remove_clk_handler: | ||
435 | acpi_remove_address_space_handler(handle, TI_PMIC_CLOCK_OPREGION_ID, | ||
436 | tps68470_pmic_clk_handler); | ||
437 | out_remove_vr_val_handler: | ||
438 | acpi_remove_address_space_handler(handle, TI_PMIC_VR_VAL_OPREGION_ID, | ||
439 | tps68470_pmic_vrval_handler); | ||
440 | out_remove_power_handler: | ||
441 | acpi_remove_address_space_handler(handle, TI_PMIC_POWER_OPREGION_ID, | ||
442 | tps68470_pmic_pwr_handler); | ||
443 | out_mutex_destroy: | ||
444 | mutex_destroy(&opregion->lock); | ||
445 | return -ENODEV; | ||
446 | } | ||
447 | |||
448 | static struct platform_driver tps68470_pmic_opregion_driver = { | ||
449 | .probe = tps68470_pmic_opregion_probe, | ||
450 | .driver = { | ||
451 | .name = "tps68470_pmic_opregion", | ||
452 | }, | ||
453 | }; | ||
454 | |||
455 | builtin_platform_driver(tps68470_pmic_opregion_driver) | ||
diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c index b4fbb9929482..ec5b0f190231 100644 --- a/drivers/acpi/x86/utils.c +++ b/drivers/acpi/x86/utils.c | |||
@@ -71,18 +71,34 @@ static const struct always_present_id always_present_ids[] = { | |||
71 | DMI_MATCH(DMI_PRODUCT_NAME, "Venue 11 Pro 7130"), | 71 | DMI_MATCH(DMI_PRODUCT_NAME, "Venue 11 Pro 7130"), |
72 | }), | 72 | }), |
73 | /* | 73 | /* |
74 | * The GPD win BIOS dated 20170320 has disabled the accelerometer, the | 74 | * The GPD win BIOS dated 20170221 has disabled the accelerometer, the |
75 | * drivers sometimes cause crashes under Windows and this is how the | 75 | * drivers sometimes cause crashes under Windows and this is how the |
76 | * manufacturer has solved this :| Note that the the DMI data is less | 76 | * manufacturer has solved this :| Note that the the DMI data is less |
77 | * generic then it seems, a board_vendor of "AMI Corporation" is quite | 77 | * generic then it seems, a board_vendor of "AMI Corporation" is quite |
78 | * rare and a board_name of "Default String" also is rare. | 78 | * rare and a board_name of "Default String" also is rare. |
79 | * | ||
80 | * Unfortunately the GPD pocket also uses these strings and its BIOS | ||
81 | * was copy-pasted from the GPD win, so it has a disabled KIOX000A | ||
82 | * node which we should not enable, thus we also check the BIOS date. | ||
79 | */ | 83 | */ |
80 | ENTRY("KIOX000A", "1", ICPU(INTEL_FAM6_ATOM_AIRMONT), { | 84 | ENTRY("KIOX000A", "1", ICPU(INTEL_FAM6_ATOM_AIRMONT), { |
81 | DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), | 85 | DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), |
82 | DMI_MATCH(DMI_BOARD_NAME, "Default string"), | 86 | DMI_MATCH(DMI_BOARD_NAME, "Default string"), |
83 | DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), | 87 | DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), |
88 | DMI_MATCH(DMI_BIOS_DATE, "02/21/2017") | ||
89 | }), | ||
90 | ENTRY("KIOX000A", "1", ICPU(INTEL_FAM6_ATOM_AIRMONT), { | ||
91 | DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), | ||
92 | DMI_MATCH(DMI_BOARD_NAME, "Default string"), | ||
93 | DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), | ||
84 | DMI_MATCH(DMI_BIOS_DATE, "03/20/2017") | 94 | DMI_MATCH(DMI_BIOS_DATE, "03/20/2017") |
85 | }), | 95 | }), |
96 | ENTRY("KIOX000A", "1", ICPU(INTEL_FAM6_ATOM_AIRMONT), { | ||
97 | DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), | ||
98 | DMI_MATCH(DMI_BOARD_NAME, "Default string"), | ||
99 | DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), | ||
100 | DMI_MATCH(DMI_BIOS_DATE, "05/25/2017") | ||
101 | }), | ||
86 | }; | 102 | }; |
87 | 103 | ||
88 | bool acpi_device_always_present(struct acpi_device *adev) | 104 | bool acpi_device_always_present(struct acpi_device *adev) |
diff --git a/include/acpi/apei.h b/include/acpi/apei.h index 1797e81a3204..680f80960c3d 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h | |||
@@ -51,7 +51,6 @@ int erst_clear(u64 record_id); | |||
51 | 51 | ||
52 | int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data); | 52 | int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data); |
53 | void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err); | 53 | void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err); |
54 | void arch_apei_flush_tlb_one(unsigned long addr); | ||
55 | 54 | ||
56 | #endif | 55 | #endif |
57 | #endif | 56 | #endif |