From e13452ac379070f038c264618e35559434252175 Mon Sep 17 00:00:00 2001 From: Rajmohan Mani Date: Fri, 28 Jul 2017 17:30:26 -0700 Subject: ACPI / PMIC: Add TI PMIC TPS68470 operation region driver The Kabylake platform coreboot (Chrome OS equivalent of BIOS) has defined 4 operation regions for the TI TPS68470 PMIC. These operation regions are to enable/disable voltage regulators, configure voltage regulators, enable/disable clocks and to configure clocks. This config adds ACPI operation region support for TI TPS68470 PMIC. TPS68470 device is an advanced power management unit that powers a Compact Camera Module (CCM), generates clocks for image sensors, drives a dual LED for flash and incorporates two LED drivers for general purpose indicators. This driver enables ACPI operation region support to control voltage regulators and clocks for the TPS68470 PMIC. Signed-off-by: Rajmohan Mani Acked-by: Sakari Ailus Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 16 ++ drivers/acpi/Makefile | 2 + drivers/acpi/pmic/tps68470_pmic.c | 455 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 473 insertions(+) create mode 100644 drivers/acpi/pmic/tps68470_pmic.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 1ce52f84dc23..e124a82df00c 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -535,4 +535,20 @@ if ARM64 source "drivers/acpi/arm64/Kconfig" endif +config TPS68470_PMIC_OPREGION + bool "ACPI operation region support for TPS68470 PMIC" + depends on MFD_TPS68470 + help + This config adds ACPI operation region support for TI TPS68470 PMIC. + TPS68470 device is an advanced power management unit that powers + a Compact Camera Module (CCM), generates clocks for image sensors, + drives a dual LED for flash and incorporates two LED drivers for + general purpose indicators. + This driver enables ACPI operation region support control voltage + regulators and clocks. + + This option is a bool as it provides an ACPI operation + region, which must be available before any of the devices + using this, are probed. + endif # ACPI diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 90265ab4437a..259e0b83acbf 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -107,6 +107,8 @@ obj-$(CONFIG_CHT_WC_PMIC_OPREGION) += pmic/intel_pmic_chtwc.o obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o +obj-$(CONFIG_TPS68470_PMIC_OPREGION) += pmic/tps68470_pmic.o + video-objs += acpi_video.o video_detect.o obj-y += dptf/ 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 @@ +/* + * TI TPS68470 PMIC operation region driver + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Author: Rajmohan Mani + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based on drivers/acpi/pmic/intel_pmic* drivers + */ + +#include +#include +#include +#include +#include + +struct tps68470_pmic_table { + u32 address; /* operation region address */ + u32 reg; /* corresponding register */ + u32 bitmask; /* bit mask for power, clock */ +}; + +#define TI_PMIC_POWER_OPREGION_ID 0xB0 +#define TI_PMIC_VR_VAL_OPREGION_ID 0xB1 +#define TI_PMIC_CLOCK_OPREGION_ID 0xB2 +#define TI_PMIC_CLKFREQ_OPREGION_ID 0xB3 + +struct tps68470_pmic_opregion { + struct mutex lock; + struct regmap *regmap; +}; + +#define S_IO_I2C_EN (BIT(0) | BIT(1)) + +static const struct tps68470_pmic_table power_table[] = { + { + .address = 0x00, + .reg = TPS68470_REG_S_I2C_CTL, + .bitmask = S_IO_I2C_EN, + /* S_I2C_CTL */ + }, + { + .address = 0x04, + .reg = TPS68470_REG_VCMCTL, + .bitmask = BIT(0), + /* VCMCTL */ + }, + { + .address = 0x08, + .reg = TPS68470_REG_VAUX1CTL, + .bitmask = BIT(0), + /* VAUX1_CTL */ + }, + { + .address = 0x0C, + .reg = TPS68470_REG_VAUX2CTL, + .bitmask = BIT(0), + /* VAUX2CTL */ + }, + { + .address = 0x10, + .reg = TPS68470_REG_VACTL, + .bitmask = BIT(0), + /* VACTL */ + }, + { + .address = 0x14, + .reg = TPS68470_REG_VDCTL, + .bitmask = BIT(0), + /* VDCTL */ + }, +}; + +/* Table to set voltage regulator value */ +static const struct tps68470_pmic_table vr_val_table[] = { + { + .address = 0x00, + .reg = TPS68470_REG_VSIOVAL, + .bitmask = TPS68470_VSIOVAL_IOVOLT_MASK, + /* TPS68470_REG_VSIOVAL */ + }, + { + .address = 0x04, + .reg = TPS68470_REG_VIOVAL, + .bitmask = TPS68470_VIOVAL_IOVOLT_MASK, + /* TPS68470_REG_VIOVAL */ + }, + { + .address = 0x08, + .reg = TPS68470_REG_VCMVAL, + .bitmask = TPS68470_VCMVAL_VCVOLT_MASK, + /* TPS68470_REG_VCMVAL */ + }, + { + .address = 0x0C, + .reg = TPS68470_REG_VAUX1VAL, + .bitmask = TPS68470_VAUX1VAL_AUX1VOLT_MASK, + /* TPS68470_REG_VAUX1VAL */ + }, + { + .address = 0x10, + .reg = TPS68470_REG_VAUX2VAL, + .bitmask = TPS68470_VAUX2VAL_AUX2VOLT_MASK, + /* TPS68470_REG_VAUX2VAL */ + }, + { + .address = 0x14, + .reg = TPS68470_REG_VAVAL, + .bitmask = TPS68470_VAVAL_AVOLT_MASK, + /* TPS68470_REG_VAVAL */ + }, + { + .address = 0x18, + .reg = TPS68470_REG_VDVAL, + .bitmask = TPS68470_VDVAL_DVOLT_MASK, + /* TPS68470_REG_VDVAL */ + }, +}; + +/* Table to configure clock frequency */ +static const struct tps68470_pmic_table clk_freq_table[] = { + { + .address = 0x00, + .reg = TPS68470_REG_POSTDIV2, + .bitmask = BIT(0) | BIT(1), + /* TPS68470_REG_POSTDIV2 */ + }, + { + .address = 0x04, + .reg = TPS68470_REG_BOOSTDIV, + .bitmask = 0x1F, + /* TPS68470_REG_BOOSTDIV */ + }, + { + .address = 0x08, + .reg = TPS68470_REG_BUCKDIV, + .bitmask = 0x0F, + /* TPS68470_REG_BUCKDIV */ + }, + { + .address = 0x0C, + .reg = TPS68470_REG_PLLSWR, + .bitmask = 0x13, + /* TPS68470_REG_PLLSWR */ + }, + { + .address = 0x10, + .reg = TPS68470_REG_XTALDIV, + .bitmask = 0xFF, + /* TPS68470_REG_XTALDIV */ + }, + { + .address = 0x14, + .reg = TPS68470_REG_PLLDIV, + .bitmask = 0xFF, + /* TPS68470_REG_PLLDIV */ + }, + { + .address = 0x18, + .reg = TPS68470_REG_POSTDIV, + .bitmask = 0x83, + /* TPS68470_REG_POSTDIV */ + }, +}; + +/* Table to configure and enable clocks */ +static const struct tps68470_pmic_table clk_table[] = { + { + .address = 0x00, + .reg = TPS68470_REG_PLLCTL, + .bitmask = 0xF5, + /* TPS68470_REG_PLLCTL */ + }, + { + .address = 0x04, + .reg = TPS68470_REG_PLLCTL2, + .bitmask = BIT(0), + /* TPS68470_REG_PLLCTL2 */ + }, + { + .address = 0x08, + .reg = TPS68470_REG_CLKCFG1, + .bitmask = TPS68470_CLKCFG1_MODE_A_MASK | + TPS68470_CLKCFG1_MODE_B_MASK, + /* TPS68470_REG_CLKCFG1 */ + }, + { + .address = 0x0C, + .reg = TPS68470_REG_CLKCFG2, + .bitmask = TPS68470_CLKCFG1_MODE_A_MASK | + TPS68470_CLKCFG1_MODE_B_MASK, + /* TPS68470_REG_CLKCFG2 */ + }, +}; + +static int pmic_get_reg_bit(u64 address, + const struct tps68470_pmic_table *table, + const unsigned int table_size, int *reg, + int *bitmask) +{ + u64 i; + + i = address / 4; + if (i >= table_size) + return -ENOENT; + + if (!reg || !bitmask) + return -EINVAL; + + *reg = table[i].reg; + *bitmask = table[i].bitmask; + + return 0; +} + +static int tps68470_pmic_get_power(struct regmap *regmap, int reg, + int bitmask, u64 *value) +{ + unsigned int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + *value = (data & bitmask) ? 1 : 0; + return 0; +} + +static int tps68470_pmic_get_vr_val(struct regmap *regmap, int reg, + int bitmask, u64 *value) +{ + unsigned int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + *value = data & bitmask; + return 0; +} + +static int tps68470_pmic_get_clk(struct regmap *regmap, int reg, + int bitmask, u64 *value) +{ + unsigned int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + *value = (data & bitmask) ? 1 : 0; + return 0; +} + +static int tps68470_pmic_get_clk_freq(struct regmap *regmap, int reg, + int bitmask, u64 *value) +{ + unsigned int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + *value = data & bitmask; + return 0; +} + +static int ti_tps68470_regmap_update_bits(struct regmap *regmap, int reg, + int bitmask, u64 value) +{ + return regmap_update_bits(regmap, reg, bitmask, value); +} + +static acpi_status tps68470_pmic_common_handler(u32 function, + acpi_physical_address address, + u32 bits, u64 *value, + void *region_context, + int (*get)(struct regmap *, + int, int, u64 *), + int (*update)(struct regmap *, + int, int, u64), + const struct tps68470_pmic_table *tbl, + unsigned int tbl_size) +{ + struct tps68470_pmic_opregion *opregion = region_context; + struct regmap *regmap = opregion->regmap; + int reg, ret, bitmask; + + if (bits != 32) + return AE_BAD_PARAMETER; + + ret = pmic_get_reg_bit(address, tbl, tbl_size, ®, &bitmask); + if (ret < 0) + return AE_BAD_PARAMETER; + + if (function == ACPI_WRITE && *value > bitmask) + return AE_BAD_PARAMETER; + + mutex_lock(&opregion->lock); + + ret = (function == ACPI_READ) ? + get(regmap, reg, bitmask, value) : + update(regmap, reg, bitmask, *value); + + mutex_unlock(&opregion->lock); + + return ret ? AE_ERROR : AE_OK; +} + +static acpi_status tps68470_pmic_cfreq_handler(u32 function, + acpi_physical_address address, + u32 bits, u64 *value, + void *handler_context, + void *region_context) +{ + return tps68470_pmic_common_handler(function, address, bits, value, + region_context, + tps68470_pmic_get_clk_freq, + ti_tps68470_regmap_update_bits, + clk_freq_table, + ARRAY_SIZE(clk_freq_table)); +} + +static acpi_status tps68470_pmic_clk_handler(u32 function, + acpi_physical_address address, u32 bits, + u64 *value, void *handler_context, + void *region_context) +{ + return tps68470_pmic_common_handler(function, address, bits, value, + region_context, + tps68470_pmic_get_clk, + ti_tps68470_regmap_update_bits, + clk_table, + ARRAY_SIZE(clk_table)); +} + +static acpi_status tps68470_pmic_vrval_handler(u32 function, + acpi_physical_address address, + u32 bits, u64 *value, + void *handler_context, + void *region_context) +{ + return tps68470_pmic_common_handler(function, address, bits, value, + region_context, + tps68470_pmic_get_vr_val, + ti_tps68470_regmap_update_bits, + vr_val_table, + ARRAY_SIZE(vr_val_table)); +} + +static acpi_status tps68470_pmic_pwr_handler(u32 function, + acpi_physical_address address, + u32 bits, u64 *value, + void *handler_context, + void *region_context) +{ + if (bits != 32) + return AE_BAD_PARAMETER; + + /* set/clear for bit 0, bits 0 and 1 together */ + if (function == ACPI_WRITE && + !(*value == 0 || *value == 1 || *value == 3)) { + return AE_BAD_PARAMETER; + } + + return tps68470_pmic_common_handler(function, address, bits, value, + region_context, + tps68470_pmic_get_power, + ti_tps68470_regmap_update_bits, + power_table, + ARRAY_SIZE(power_table)); +} + +static int tps68470_pmic_opregion_probe(struct platform_device *pdev) +{ + struct regmap *tps68470_regmap = dev_get_drvdata(pdev->dev.parent); + acpi_handle handle = ACPI_HANDLE(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct tps68470_pmic_opregion *opregion; + acpi_status status; + + if (!dev || !tps68470_regmap) { + dev_warn(dev, "dev or regmap is NULL\n"); + return -EINVAL; + } + + if (!handle) { + dev_warn(dev, "acpi handle is NULL\n"); + return -ENODEV; + } + + opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL); + if (!opregion) + return -ENOMEM; + + mutex_init(&opregion->lock); + opregion->regmap = tps68470_regmap; + + status = acpi_install_address_space_handler(handle, + TI_PMIC_POWER_OPREGION_ID, + tps68470_pmic_pwr_handler, + NULL, opregion); + if (ACPI_FAILURE(status)) + goto out_mutex_destroy; + + status = acpi_install_address_space_handler(handle, + TI_PMIC_VR_VAL_OPREGION_ID, + tps68470_pmic_vrval_handler, + NULL, opregion); + if (ACPI_FAILURE(status)) + goto out_remove_power_handler; + + status = acpi_install_address_space_handler(handle, + TI_PMIC_CLOCK_OPREGION_ID, + tps68470_pmic_clk_handler, + NULL, opregion); + if (ACPI_FAILURE(status)) + goto out_remove_vr_val_handler; + + status = acpi_install_address_space_handler(handle, + TI_PMIC_CLKFREQ_OPREGION_ID, + tps68470_pmic_cfreq_handler, + NULL, opregion); + if (ACPI_FAILURE(status)) + goto out_remove_clk_handler; + + return 0; + +out_remove_clk_handler: + acpi_remove_address_space_handler(handle, TI_PMIC_CLOCK_OPREGION_ID, + tps68470_pmic_clk_handler); +out_remove_vr_val_handler: + acpi_remove_address_space_handler(handle, TI_PMIC_VR_VAL_OPREGION_ID, + tps68470_pmic_vrval_handler); +out_remove_power_handler: + acpi_remove_address_space_handler(handle, TI_PMIC_POWER_OPREGION_ID, + tps68470_pmic_pwr_handler); +out_mutex_destroy: + mutex_destroy(&opregion->lock); + return -ENODEV; +} + +static struct platform_driver tps68470_pmic_opregion_driver = { + .probe = tps68470_pmic_opregion_probe, + .driver = { + .name = "tps68470_pmic_opregion", + }, +}; + +builtin_platform_driver(tps68470_pmic_opregion_driver) -- cgit v1.2.3 From 095f613c6b386a1704b73a549e9ba66c1d5381ae Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 25 Sep 2017 02:06:19 -0600 Subject: ACPI / APEI: adjust a local variable type in ghes_ioremap_pfn_irq() Match up with what 7edda0886b ("acpi: apei: handle SEA notification type for ARMv8") did for ghes_ioremap_pfn_nmi(). Signed-off-by: Jan Beulich Reviewed-by: Borislav Petkov Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/ghes.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 3c3a37b8503b..69ef0b6bf25d 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -174,7 +174,8 @@ static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) static void __iomem *ghes_ioremap_pfn_irq(u64 pfn) { - unsigned long vaddr, paddr; + unsigned long vaddr; + phys_addr_t paddr; pgprot_t prot; vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr); -- cgit v1.2.3 From 1c3b44c0f40d22b43fa58fe7081ca36e38449114 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 19 Oct 2017 13:23:21 +0200 Subject: ACPI / x86: Extend KIOX000A quirk to cover all affected BIOS versions Recently produced GPD win devices have a new BIOS, research into the changes in this BIOS has found a ChangeLog which shows that the disabling of the KIOX000A node has been done starting with the 20170221 version. Unfortunately the GPD pocket uses the exact same DMI strings as the win and its BIOS was copy-pasted from the GPD win, so it has a disabled KIOX000A node which we should not enable, so we need to check for the exact BIOS date. This commit adds 2 extra entries to the always_present_ids quirk table with bios_date matches for the older also affected and the latest BIOS. Reported-by: ReddestDream Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki --- drivers/acpi/x86/utils.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) 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[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Venue 11 Pro 7130"), }), /* - * The GPD win BIOS dated 20170320 has disabled the accelerometer, the + * The GPD win BIOS dated 20170221 has disabled the accelerometer, the * drivers sometimes cause crashes under Windows and this is how the * manufacturer has solved this :| Note that the the DMI data is less * generic then it seems, a board_vendor of "AMI Corporation" is quite * rare and a board_name of "Default String" also is rare. + * + * Unfortunately the GPD pocket also uses these strings and its BIOS + * was copy-pasted from the GPD win, so it has a disabled KIOX000A + * node which we should not enable, thus we also check the BIOS date. */ + ENTRY("KIOX000A", "1", ICPU(INTEL_FAM6_ATOM_AIRMONT), { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Default string"), + DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), + DMI_MATCH(DMI_BIOS_DATE, "02/21/2017") + }), ENTRY("KIOX000A", "1", ICPU(INTEL_FAM6_ATOM_AIRMONT), { DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), DMI_MATCH(DMI_BOARD_NAME, "Default string"), DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), DMI_MATCH(DMI_BIOS_DATE, "03/20/2017") }), + ENTRY("KIOX000A", "1", ICPU(INTEL_FAM6_ATOM_AIRMONT), { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Default string"), + DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), + DMI_MATCH(DMI_BIOS_DATE, "05/25/2017") + }), }; bool acpi_device_always_present(struct acpi_device *adev) -- cgit v1.2.3 From c49870e89f4d2c21c76ebe90568246bb0f3572b7 Mon Sep 17 00:00:00 2001 From: Dongjiu Geng Date: Tue, 17 Oct 2017 16:02:20 +0800 Subject: ACPI / APEI: remove the unused dead-code for SEA/NMI notification type For the SEA notification, the two functions ghes_sea_add() and ghes_sea_remove() are only called when CONFIG_ACPI_APEI_SEA is defined. If not, it will return errors in the ghes_probe() and not continue. If the probe is failed, the ghes_sea_remove() also has no chance to be called. Hence, remove the unnecessary handling when CONFIG_ACPI_APEI_SEA is not defined. For the NMI notification, it has the same issue as SEA notification, so also remove the unused dead-code for it. Signed-off-by: Dongjiu Geng Tested-by: Tyler Baicar Reviewed-by: Borislav Petkov Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/ghes.c | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 69ef0b6bf25d..cb7aceae3553 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -852,17 +852,8 @@ static void ghes_sea_remove(struct ghes *ghes) synchronize_rcu(); } #else /* CONFIG_ACPI_APEI_SEA */ -static inline void ghes_sea_add(struct ghes *ghes) -{ - pr_err(GHES_PFX "ID: %d, trying to add SEA notification which is not supported\n", - ghes->generic->header.source_id); -} - -static inline void ghes_sea_remove(struct ghes *ghes) -{ - pr_err(GHES_PFX "ID: %d, trying to remove SEA notification which is not supported\n", - ghes->generic->header.source_id); -} +static inline void ghes_sea_add(struct ghes *ghes) { } +static inline void ghes_sea_remove(struct ghes *ghes) { } #endif /* CONFIG_ACPI_APEI_SEA */ #ifdef CONFIG_HAVE_ACPI_APEI_NMI @@ -1064,23 +1055,9 @@ static void ghes_nmi_init_cxt(void) init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); } #else /* CONFIG_HAVE_ACPI_APEI_NMI */ -static inline void ghes_nmi_add(struct ghes *ghes) -{ - pr_err(GHES_PFX "ID: %d, trying to add NMI notification which is not supported!\n", - ghes->generic->header.source_id); - BUG(); -} - -static inline void ghes_nmi_remove(struct ghes *ghes) -{ - pr_err(GHES_PFX "ID: %d, trying to remove NMI notification which is not supported!\n", - ghes->generic->header.source_id); - BUG(); -} - -static inline void ghes_nmi_init_cxt(void) -{ -} +static inline void ghes_nmi_add(struct ghes *ghes) { } +static inline void ghes_nmi_remove(struct ghes *ghes) { } +static inline void ghes_nmi_init_cxt(void) { } #endif /* CONFIG_HAVE_ACPI_APEI_NMI */ static int ghes_probe(struct platform_device *ghes_dev) -- cgit v1.2.3 From 4f89fa286f6729312e227e7c2d764e8e7b9d340e Mon Sep 17 00:00:00 2001 From: James Morse Date: Mon, 6 Nov 2017 18:44:24 +0000 Subject: ACPI / APEI: Replace ioremap_page_range() with fixmap Replace ghes_io{re,un}map_pfn_{nmi,irq}()s use of ioremap_page_range() with __set_fixmap() as ioremap_page_range() may sleep to allocate a new level of page-table, even if its passed an existing final-address to use in the mapping. The GHES driver can only be enabled for architectures that select HAVE_ACPI_APEI: Add fixmap entries to both x86 and arm64. clear_fixmap() does the TLB invalidation in __set_fixmap() for arm64 and __set_pte_vaddr() for x86. In each case its the same as the respective arch_apei_flush_tlb_one(). Reported-by: Fengguang Wu Suggested-by: Linus Torvalds Signed-off-by: James Morse Reviewed-by: Borislav Petkov Tested-by: Tyler Baicar Tested-by: Toshi Kani [ For the arm64 bits: ] Acked-by: Will Deacon [ For the x86 bits: ] Acked-by: Ingo Molnar Signed-off-by: Rafael J. Wysocki Cc: All applicable --- arch/arm64/include/asm/fixmap.h | 7 +++++++ arch/x86/include/asm/fixmap.h | 6 ++++++ drivers/acpi/apei/ghes.c | 44 +++++++++++++---------------------------- 3 files changed, 27 insertions(+), 30 deletions(-) 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 { FIX_EARLYCON_MEM_BASE, FIX_TEXT_POKE0, + +#ifdef CONFIG_ACPI_APEI_GHES + /* Used for GHES mapping from assorted contexts */ + FIX_APEI_GHES_IRQ, + FIX_APEI_GHES_NMI, +#endif /* CONFIG_ACPI_APEI_GHES */ + __end_of_permanent_fixed_addresses, /* 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 { FIX_GDT_REMAP_BEGIN, FIX_GDT_REMAP_END = FIX_GDT_REMAP_BEGIN + NR_CPUS - 1, +#ifdef CONFIG_ACPI_APEI_GHES + /* Used for GHES mapping from assorted contexts */ + FIX_APEI_GHES_IRQ, + FIX_APEI_GHES_NMI, +#endif + __end_of_permanent_fixed_addresses, /* diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index cb7aceae3553..572b6c7303ed 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -112,7 +113,7 @@ static DEFINE_MUTEX(ghes_list_mutex); * Because the memory area used to transfer hardware error information * from BIOS to Linux can be determined only in NMI, IRQ or timer * handler, but general ioremap can not be used in atomic context, so - * a special version of atomic ioremap is implemented for that. + * the fixmap is used instead. */ /* @@ -126,8 +127,8 @@ static DEFINE_MUTEX(ghes_list_mutex); /* virtual memory area for atomic ioremap */ static struct vm_struct *ghes_ioremap_area; /* - * These 2 spinlock is used to prevent atomic ioremap virtual memory - * area from being mapped simultaneously. + * These 2 spinlocks are used to prevent the fixmap entries from being used + * simultaneously. */ static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); @@ -159,53 +160,36 @@ static void ghes_ioremap_exit(void) static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) { - unsigned long vaddr; phys_addr_t paddr; pgprot_t prot; - vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr); - paddr = pfn << PAGE_SHIFT; prot = arch_apei_get_mem_attribute(paddr); - ioremap_page_range(vaddr, vaddr + PAGE_SIZE, paddr, prot); + __set_fixmap(FIX_APEI_GHES_NMI, paddr, prot); - return (void __iomem *)vaddr; + return (void __iomem *) fix_to_virt(FIX_APEI_GHES_NMI); } static void __iomem *ghes_ioremap_pfn_irq(u64 pfn) { - unsigned long vaddr; phys_addr_t paddr; pgprot_t prot; - vaddr = (unsigned long)GHES_IOREMAP_IRQ_PAGE(ghes_ioremap_area->addr); - paddr = pfn << PAGE_SHIFT; prot = arch_apei_get_mem_attribute(paddr); + __set_fixmap(FIX_APEI_GHES_IRQ, paddr, prot); - ioremap_page_range(vaddr, vaddr + PAGE_SIZE, paddr, prot); - - return (void __iomem *)vaddr; + return (void __iomem *) fix_to_virt(FIX_APEI_GHES_IRQ); } -static void ghes_iounmap_nmi(void __iomem *vaddr_ptr) +static void ghes_iounmap_nmi(void) { - unsigned long vaddr = (unsigned long __force)vaddr_ptr; - void *base = ghes_ioremap_area->addr; - - BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_NMI_PAGE(base)); - unmap_kernel_range_noflush(vaddr, PAGE_SIZE); - arch_apei_flush_tlb_one(vaddr); + clear_fixmap(FIX_APEI_GHES_NMI); } -static void ghes_iounmap_irq(void __iomem *vaddr_ptr) +static void ghes_iounmap_irq(void) { - unsigned long vaddr = (unsigned long __force)vaddr_ptr; - void *base = ghes_ioremap_area->addr; - - BUG_ON(vaddr != (unsigned long)GHES_IOREMAP_IRQ_PAGE(base)); - unmap_kernel_range_noflush(vaddr, PAGE_SIZE); - arch_apei_flush_tlb_one(vaddr); + clear_fixmap(FIX_APEI_GHES_IRQ); } static int ghes_estatus_pool_init(void) @@ -361,10 +345,10 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, paddr += trunk; buffer += trunk; if (in_nmi) { - ghes_iounmap_nmi(vaddr); + ghes_iounmap_nmi(); raw_spin_unlock(&ghes_ioremap_lock_nmi); } else { - ghes_iounmap_irq(vaddr); + ghes_iounmap_irq(); spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); } } -- cgit v1.2.3 From 520e18a5080d2c444a03280d99c8a35cb667d321 Mon Sep 17 00:00:00 2001 From: James Morse Date: Mon, 6 Nov 2017 18:44:25 +0000 Subject: ACPI / APEI: Remove ghes_ioremap_area Now that nothing is using the ghes_ioremap_area pages, rip them out. Signed-off-by: James Morse Reviewed-by: Borislav Petkov Tested-by: Tyler Baicar Signed-off-by: Rafael J. Wysocki Cc: All applicable --- drivers/acpi/apei/ghes.c | 39 ++------------------------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 572b6c7303ed..f14695e744d0 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -114,19 +114,7 @@ static DEFINE_MUTEX(ghes_list_mutex); * from BIOS to Linux can be determined only in NMI, IRQ or timer * handler, but general ioremap can not be used in atomic context, so * the fixmap is used instead. - */ - -/* - * Two virtual pages are used, one for IRQ/PROCESS context, the other for - * NMI context (optionally). - */ -#define GHES_IOREMAP_PAGES 2 -#define GHES_IOREMAP_IRQ_PAGE(base) (base) -#define GHES_IOREMAP_NMI_PAGE(base) ((base) + PAGE_SIZE) - -/* virtual memory area for atomic ioremap */ -static struct vm_struct *ghes_ioremap_area; -/* + * * These 2 spinlocks are used to prevent the fixmap entries from being used * simultaneously. */ @@ -141,23 +129,6 @@ static atomic_t ghes_estatus_cache_alloced; static int ghes_panic_timeout __read_mostly = 30; -static int ghes_ioremap_init(void) -{ - ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES, - VM_IOREMAP, VMALLOC_START, VMALLOC_END); - if (!ghes_ioremap_area) { - pr_err(GHES_PFX "Failed to allocate virtual memory area for atomic ioremap.\n"); - return -ENOMEM; - } - - return 0; -} - -static void ghes_ioremap_exit(void) -{ - free_vm_area(ghes_ioremap_area); -} - static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) { phys_addr_t paddr; @@ -1247,13 +1218,9 @@ static int __init ghes_init(void) ghes_nmi_init_cxt(); - rc = ghes_ioremap_init(); - if (rc) - goto err; - rc = ghes_estatus_pool_init(); if (rc) - goto err_ioremap_exit; + goto err; rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE * GHES_ESTATUS_CACHE_ALLOCED_MAX); @@ -1277,8 +1244,6 @@ static int __init ghes_init(void) return 0; err_pool_exit: ghes_estatus_pool_exit(); -err_ioremap_exit: - ghes_ioremap_exit(); err: return rc; } -- cgit v1.2.3 From 18b4b276b490a8b9f86c512de8a6054c27bb87c2 Mon Sep 17 00:00:00 2001 From: James Morse Date: Mon, 6 Nov 2017 18:44:26 +0000 Subject: arm64: mm: Remove arch_apei_flush_tlb_one() Nothing calls arch_apei_flush_tlb_one() anymore, instead relying on __set_fixmap() to do the invalidation. Remove it. Move the IPI-considered-harmful comment to __set_fixmap(). Signed-off-by: James Morse Acked-by: Will Deacon Tested-by: Tyler Baicar Signed-off-by: Rafael J. Wysocki Cc: All applicable --- arch/arm64/include/asm/acpi.h | 12 ------------ arch/arm64/mm/mmu.c | 4 ++++ 2 files changed, 4 insertions(+), 12 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) */ #define acpi_disable_cmcff 1 pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr); - -/* - * Despite its name, this function must still broadcast the TLB - * invalidation in order to ensure other CPUs don't end up with junk - * entries as a result of speculation. Unusually, its also called in - * IRQ context (ghes_iounmap_irq) so if we ever need to use IPIs for - * TLB broadcasting, then we're in trouble here. - */ -static inline void arch_apei_flush_tlb_one(unsigned long addr) -{ - flush_tlb_kernel_range(addr, addr + PAGE_SIZE); -} #endif /* CONFIG_ACPI_APEI */ #ifdef CONFIG_ACPI_NUMA 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) } } +/* + * Unusually, this is also called in IRQ context (ghes_iounmap_irq) so if we + * ever need to use IPIs for TLB broadcasting, then we're in trouble here. + */ void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t flags) { -- cgit v1.2.3 From 4a75aeacda3c2455954596593d89187df5420d0a Mon Sep 17 00:00:00 2001 From: James Morse Date: Mon, 6 Nov 2017 18:44:27 +0000 Subject: ACPI / APEI: Remove arch_apei_flush_tlb_one() Nothing calls arch_apei_flush_tlb_one() anymore, instead relying on __set_pte_vaddr() to do the invalidation when called from clear_fixmap() Remove arch_apei_flush_tlb_one(). Signed-off-by: James Morse Reviewed-by: Borislav Petkov Signed-off-by: Rafael J. Wysocki Cc: All applicable --- arch/x86/kernel/acpi/apei.c | 5 ----- include/acpi/apei.h | 1 - 2 files changed, 6 deletions(-) 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) apei_mce_report_mem_error(sev, mem_err); #endif } - -void arch_apei_flush_tlb_one(unsigned long addr) -{ - __flush_tlb_one(addr); -} diff --git a/include/acpi/apei.h b/include/acpi/apei.h index c46694abea28..82c451698c98 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -50,7 +50,6 @@ int erst_clear(u64 record_id); int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data); void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err); -void arch_apei_flush_tlb_one(unsigned long addr); #endif #endif -- cgit v1.2.3 From c0041d40ba6a352e5a5304d656e8a26e2fba1f63 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 2 Nov 2017 12:42:50 +0100 Subject: APEI / ERST: use 64-bit timestamps 32-bit timestamps are deprecated in the kernel, so we should not use get_seconds(). In this case, the 'struct cper_record_header' structure already contains a 64-bit field, so the only required change is to use the safe ktime_get_real_seconds() interface as a replacement. Signed-off-by: Arnd Bergmann Reviewed-by: Thomas Gleixner Signed-off-by: Rafael J. Wysocki --- drivers/acpi/apei/erst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) rcd->hdr.error_severity = CPER_SEV_FATAL; /* timestamp valid. platform_id, partition_id are invalid */ rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; - rcd->hdr.timestamp = get_seconds(); + rcd->hdr.timestamp = ktime_get_real_seconds(); rcd->hdr.record_length = sizeof(*rcd) + record->size; rcd->hdr.creator_id = CPER_CREATOR_PSTORE; rcd->hdr.notification_type = CPER_NOTIFY_MCE; -- cgit v1.2.3