diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-18 15:45:33 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-18 15:45:33 -0800 |
commit | 8421c60446290c0fef1858a806261871a40ebf76 (patch) | |
tree | eab366090e5af422f2ebd014886ae7f0de7f33f1 | |
parent | f7dd3b1734ea335fea01f103d48b3de26ea0d335 (diff) | |
parent | 83da6b59919a71a1a97ce9863aa0267eaf6d496c (diff) |
Merge tag 'platform-drivers-x86-v4.10-2' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86
Pull more x86 platform driver updates from Darren Hart:
"Move and add registration for the mlx-platform driver. Introduce
button and lid drivers for the surface3 (different from the
surface3-pro). Add BXT PMIC TMU support. Add Y700 to existing
ideapad-laptop quirk.
Summary:
ideapad-laptop:
- Add Y700 15-ACZ to no_hw_rfkill DMI list
surface3_button:
- Introduce button support for the Surface 3
surface3-wmi:
- Add custom surface3 platform device for controlling LID
- Balance locking on error path
mlx-platform:
- Add mlxcpld-hotplug driver registration
- Fix semicolon.cocci warnings
- Move module from arch/x86
platform/x86:
- Add Whiskey Cove PMIC TMU support"
* tag 'platform-drivers-x86-v4.10-2' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86:
platform/x86: surface3-wmi: Balance locking on error path
platform/x86: Add Whiskey Cove PMIC TMU support
platform/x86: ideapad-laptop: Add Y700 15-ACZ to no_hw_rfkill DMI list
platform/x86: Introduce button support for the Surface 3
platform/x86: Add custom surface3 platform device for controlling LID
platform/x86: mlx-platform: Add mlxcpld-hotplug driver registration
platform/x86: mlx-platform: Fix semicolon.cocci warnings
platform/x86: mlx-platform: Move module from arch/x86
-rw-r--r-- | MAINTAINERS | 2 | ||||
-rw-r--r-- | arch/x86/Kconfig | 12 | ||||
-rw-r--r-- | arch/x86/platform/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/platform/mellanox/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/intel_soc_pmic_bxtwc.c | 38 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 40 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 4 | ||||
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 7 | ||||
-rw-r--r-- | drivers/platform/x86/intel_bxtwc_tmu.c | 162 | ||||
-rw-r--r-- | drivers/platform/x86/mlx-platform.c (renamed from arch/x86/platform/mellanox/mlx-platform.c) | 101 | ||||
-rw-r--r-- | drivers/platform/x86/surface3-wmi.c | 297 | ||||
-rw-r--r-- | drivers/platform/x86/surface3_button.c | 250 | ||||
-rw-r--r-- | include/linux/mfd/intel_soc_pmic.h | 1 |
13 files changed, 897 insertions, 19 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index f39414662ab6..f6eb97b35e0f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8070,7 +8070,7 @@ MELLANOX PLATFORM DRIVER M: Vadim Pasternak <vadimp@mellanox.com> L: platform-driver-x86@vger.kernel.org S: Supported -F: arch/x86/platform/mellanox/mlx-platform.c +F: drivers/platform/x86/mlx-platform.c MELLANOX MLX CPLD HOTPLUG DRIVER M: Vadim Pasternak <vadimp@mellanox.com> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index dd47e60aabf5..64024c999531 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -555,18 +555,6 @@ config X86_INTEL_QUARK Say Y here if you have a Quark based system such as the Arduino compatible Intel Galileo. -config MLX_PLATFORM - tristate "Mellanox Technologies platform support" - depends on X86_64 - depends on X86_EXTENDED_PLATFORM - ---help--- - This option enables system support for the Mellanox Technologies - platform. - - Say Y here if you are building a kernel for Mellanox system. - - Otherwise, say N. - config X86_INTEL_LPSS bool "Intel Low Power Subsystem Support" depends on X86 && ACPI diff --git a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile index 3c3c19ea94df..184842ef332e 100644 --- a/arch/x86/platform/Makefile +++ b/arch/x86/platform/Makefile @@ -8,7 +8,6 @@ obj-y += iris/ obj-y += intel/ obj-y += intel-mid/ obj-y += intel-quark/ -obj-y += mellanox/ obj-y += olpc/ obj-y += scx200/ obj-y += sfi/ diff --git a/arch/x86/platform/mellanox/Makefile b/arch/x86/platform/mellanox/Makefile deleted file mode 100644 index f43c93188a1d..000000000000 --- a/arch/x86/platform/mellanox/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o diff --git a/drivers/mfd/intel_soc_pmic_bxtwc.c b/drivers/mfd/intel_soc_pmic_bxtwc.c index f9a8c5203873..b99772b804d9 100644 --- a/drivers/mfd/intel_soc_pmic_bxtwc.c +++ b/drivers/mfd/intel_soc_pmic_bxtwc.c @@ -42,6 +42,7 @@ #define BXTWC_GPIOIRQ0 0x4E0B #define BXTWC_GPIOIRQ1 0x4E0C #define BXTWC_CRITIRQ 0x4E0D +#define BXTWC_TMUIRQ 0x4FB6 /* Interrupt MASK Registers */ #define BXTWC_MIRQLVL1 0x4E0E @@ -59,6 +60,7 @@ #define BXTWC_MGPIO0IRQ 0x4E19 #define BXTWC_MGPIO1IRQ 0x4E1A #define BXTWC_MCRITIRQ 0x4E1B +#define BXTWC_MTMUIRQ 0x4FB7 /* Whiskey Cove PMIC share same ACPI ID between different platforms */ #define BROXTON_PMIC_WC_HRV 4 @@ -92,6 +94,7 @@ enum bxtwc_irqs_level2 { BXTWC_GPIO0_IRQ, BXTWC_GPIO1_IRQ, BXTWC_CRIT_IRQ, + BXTWC_TMU_IRQ, }; static const struct regmap_irq bxtwc_regmap_irqs[] = { @@ -120,6 +123,10 @@ static const struct regmap_irq bxtwc_regmap_irqs_level2[] = { REGMAP_IRQ_REG(BXTWC_CRIT_IRQ, 9, 0x03), }; +static const struct regmap_irq bxtwc_regmap_irqs_tmu[] = { + REGMAP_IRQ_REG(BXTWC_TMU_IRQ, 0, 0x06), +}; + static struct regmap_irq_chip bxtwc_regmap_irq_chip = { .name = "bxtwc_irq_chip", .status_base = BXTWC_IRQLVL1, @@ -138,6 +145,15 @@ static struct regmap_irq_chip bxtwc_regmap_irq_chip_level2 = { .num_regs = 10, }; +static struct regmap_irq_chip bxtwc_regmap_irq_chip_tmu = { + .name = "bxtwc_irq_chip_tmu", + .status_base = BXTWC_TMUIRQ, + .mask_base = BXTWC_MTMUIRQ, + .irqs = bxtwc_regmap_irqs_tmu, + .num_irqs = ARRAY_SIZE(bxtwc_regmap_irqs_tmu), + .num_regs = 1, +}; + static struct resource gpio_resources[] = { DEFINE_RES_IRQ_NAMED(BXTWC_GPIO0_IRQ, "GPIO0"), DEFINE_RES_IRQ_NAMED(BXTWC_GPIO1_IRQ, "GPIO1"), @@ -166,6 +182,10 @@ static struct resource bcu_resources[] = { DEFINE_RES_IRQ_NAMED(BXTWC_BCU_IRQ, "BCU"), }; +static struct resource tmu_resources[] = { + DEFINE_RES_IRQ_NAMED(BXTWC_TMU_IRQ, "TMU"), +}; + static struct mfd_cell bxt_wc_dev[] = { { .name = "bxt_wcove_gpadc", @@ -193,6 +213,12 @@ static struct mfd_cell bxt_wc_dev[] = { .resources = bcu_resources, }, { + .name = "bxt_wcove_tmu", + .num_resources = ARRAY_SIZE(tmu_resources), + .resources = tmu_resources, + }, + + { .name = "bxt_wcove_gpio", .num_resources = ARRAY_SIZE(gpio_resources), .resources = gpio_resources, @@ -402,6 +428,15 @@ static int bxtwc_probe(struct platform_device *pdev) goto err_irq_chip_level2; } + ret = regmap_add_irq_chip(pmic->regmap, pmic->irq, + IRQF_ONESHOT | IRQF_SHARED, + 0, &bxtwc_regmap_irq_chip_tmu, + &pmic->irq_chip_data_tmu); + if (ret) { + dev_err(&pdev->dev, "Failed to add TMU IRQ chip\n"); + goto err_irq_chip_tmu; + } + ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, bxt_wc_dev, ARRAY_SIZE(bxt_wc_dev), NULL, 0, NULL); @@ -431,6 +466,8 @@ static int bxtwc_probe(struct platform_device *pdev) err_sysfs: mfd_remove_devices(&pdev->dev); err_mfd: + regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_tmu); +err_irq_chip_tmu: regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_level2); err_irq_chip_level2: regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); @@ -446,6 +483,7 @@ static int bxtwc_remove(struct platform_device *pdev) mfd_remove_devices(&pdev->dev); regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data); regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_level2); + regmap_del_irq_chip(pmic->irq, pmic->irq_chip_data_tmu); return 0; } diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 185376901d9c..5fe8be089b8b 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -363,6 +363,18 @@ config IDEAPAD_LAPTOP This is a driver for Lenovo IdeaPad netbooks contains drivers for rfkill switch, hotkey, fan control and backlight control. +config SURFACE3_WMI + tristate "Surface 3 WMI Driver" + depends on ACPI_WMI + depends on DMI + depends on INPUT + depends on SPI + ---help--- + Say Y here if you have a Surface 3. + + To compile this driver as a module, choose M here: the module will + be called surface3-wmi. + config THINKPAD_ACPI tristate "ThinkPad ACPI Laptop Extras" depends on ACPI @@ -1005,12 +1017,27 @@ config INTEL_PMC_IPC The PMC is an ARC processor which defines IPC commands for communication with other entities in the CPU. +config INTEL_BXTWC_PMIC_TMU + tristate "Intel BXT Whiskey Cove TMU Driver" + depends on REGMAP + depends on INTEL_SOC_PMIC && INTEL_PMC_IPC + ---help--- + Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature. + This driver enables the alarm wakeup functionality in the TMU unit + of Whiskey Cove PMIC. + config SURFACE_PRO3_BUTTON tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet" depends on ACPI && INPUT ---help--- This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet. +config SURFACE_3_BUTTON + tristate "Power/home/volume buttons driver for Microsoft Surface 3 tablet" + depends on ACPI && KEYBOARD_GPIO + ---help--- + This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet. + config INTEL_PUNIT_IPC tristate "Intel P-Unit IPC Driver" ---help--- @@ -1028,10 +1055,21 @@ config INTEL_TELEMETRY directly via debugfs files. Various tools may use this interface for SoC state monitoring. +config MLX_PLATFORM + tristate "Mellanox Technologies platform support" + depends on X86_64 + ---help--- + This option enables system support for the Mellanox Technologies + platform. The Mellanox systems provide data center networking + solutions based on Virtual Protocol Interconnect (VPI) technology + enable seamless connectivity to 56/100Gb/s InfiniBand or 10/40/56GbE + connection. + + If you have a Mellanox system, say Y or M here. + config MLX_CPLD_PLATFORM tristate "Mellanox platform hotplug driver support" default n - depends on MLX_PLATFORM select HWMON select I2C ---help--- diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 1f06b6339cf7..d4111f0f8a78 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ACPI_WMI) += wmi.o obj-$(CONFIG_MSI_WMI) += msi-wmi.o +obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o # toshiba_acpi must link after wmi to ensure that wmi devices are found @@ -66,9 +67,12 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o +obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o +obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ intel_telemetry_pltdrv.o \ intel_telemetry_debugfs.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o +obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_MLX_CPLD_PLATFORM) += mlxcpld-hotplug.o diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index a7614fc542b5..410741acb3c9 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -871,6 +871,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { }, }, { + .ident = "Lenovo ideapad Y700-15ACZ", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ACZ"), + }, + }, + { .ident = "Lenovo ideapad Y700-15ISK", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), diff --git a/drivers/platform/x86/intel_bxtwc_tmu.c b/drivers/platform/x86/intel_bxtwc_tmu.c new file mode 100644 index 000000000000..e202abd5b0df --- /dev/null +++ b/drivers/platform/x86/intel_bxtwc_tmu.c @@ -0,0 +1,162 @@ +/* + * intel_bxtwc_tmu.c - Intel BXT Whiskey Cove PMIC TMU driver + * + * Copyright (C) 2016 Intel Corporation. All rights reserved. + * + * This driver adds TMU (Time Management Unit) support for Intel BXT platform. + * It enables the alarm wake-up functionality in the TMU unit of Whiskey Cove + * PMIC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/mfd/intel_soc_pmic.h> + +#define BXTWC_TMUIRQ 0x4fb6 +#define BXTWC_MIRQLVL1 0x4e0e +#define BXTWC_MTMUIRQ_REG 0x4fb7 +#define BXTWC_MIRQLVL1_MTMU BIT(1) +#define BXTWC_TMU_WK_ALRM BIT(1) +#define BXTWC_TMU_SYS_ALRM BIT(2) +#define BXTWC_TMU_ALRM_MASK (BXTWC_TMU_WK_ALRM | BXTWC_TMU_SYS_ALRM) +#define BXTWC_TMU_ALRM_IRQ (BXTWC_TMU_WK_ALRM | BXTWC_TMU_SYS_ALRM) + +struct wcove_tmu { + int irq; + struct device *dev; + struct regmap *regmap; +}; + +static irqreturn_t bxt_wcove_tmu_irq_handler(int irq, void *data) +{ + struct wcove_tmu *wctmu = data; + unsigned int tmu_irq; + + /* Read TMU interrupt reg */ + regmap_read(wctmu->regmap, BXTWC_TMUIRQ, &tmu_irq); + if (tmu_irq & BXTWC_TMU_ALRM_IRQ) { + /* clear TMU irq */ + regmap_write(wctmu->regmap, BXTWC_TMUIRQ, tmu_irq); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static int bxt_wcove_tmu_probe(struct platform_device *pdev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + struct regmap_irq_chip_data *regmap_irq_chip; + struct wcove_tmu *wctmu; + int ret, virq, irq; + + wctmu = devm_kzalloc(&pdev->dev, sizeof(*wctmu), GFP_KERNEL); + if (!wctmu) + return -ENOMEM; + + wctmu->dev = &pdev->dev; + wctmu->regmap = pmic->regmap; + + irq = platform_get_irq(pdev, 0); + + if (irq < 0) { + dev_err(&pdev->dev, "invalid irq %d\n", irq); + return irq; + } + + regmap_irq_chip = pmic->irq_chip_data_tmu; + virq = regmap_irq_get_virq(regmap_irq_chip, irq); + if (virq < 0) { + dev_err(&pdev->dev, + "failed to get virtual interrupt=%d\n", irq); + return virq; + } + + ret = devm_request_threaded_irq(&pdev->dev, virq, + NULL, bxt_wcove_tmu_irq_handler, + IRQF_ONESHOT, "bxt_wcove_tmu", wctmu); + if (ret) { + dev_err(&pdev->dev, "request irq failed: %d,virq: %d\n", + ret, virq); + return ret; + } + wctmu->irq = virq; + + /* Enable TMU interrupts */ + regmap_update_bits(wctmu->regmap, BXTWC_MIRQLVL1, + BXTWC_MIRQLVL1_MTMU, 0); + + /* Unmask TMU second level Wake & System alarm */ + regmap_update_bits(wctmu->regmap, BXTWC_MTMUIRQ_REG, + BXTWC_TMU_ALRM_MASK, 0); + + platform_set_drvdata(pdev, wctmu); + return 0; +} + +static int bxt_wcove_tmu_remove(struct platform_device *pdev) +{ + struct wcove_tmu *wctmu = platform_get_drvdata(pdev); + unsigned int val; + + /* Mask TMU interrupts */ + regmap_read(wctmu->regmap, BXTWC_MIRQLVL1, &val); + regmap_write(wctmu->regmap, BXTWC_MIRQLVL1, + val | BXTWC_MIRQLVL1_MTMU); + regmap_read(wctmu->regmap, BXTWC_MTMUIRQ_REG, &val); + regmap_write(wctmu->regmap, BXTWC_MTMUIRQ_REG, + val | BXTWC_TMU_ALRM_MASK); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int bxtwc_tmu_suspend(struct device *dev) +{ + struct wcove_tmu *wctmu = dev_get_drvdata(dev); + + enable_irq_wake(wctmu->irq); + return 0; +} + +static int bxtwc_tmu_resume(struct device *dev) +{ + struct wcove_tmu *wctmu = dev_get_drvdata(dev); + + disable_irq_wake(wctmu->irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(bxtwc_tmu_pm_ops, bxtwc_tmu_suspend, bxtwc_tmu_resume); + +static const struct platform_device_id bxt_wcove_tmu_id_table[] = { + { .name = "bxt_wcove_tmu" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, bxt_wcove_tmu_id_table); + +static struct platform_driver bxt_wcove_tmu_driver = { + .probe = bxt_wcove_tmu_probe, + .remove = bxt_wcove_tmu_remove, + .driver = { + .name = "bxt_wcove_tmu", + .pm = &bxtwc_tmu_pm_ops, + }, + .id_table = bxt_wcove_tmu_id_table, +}; + +module_platform_driver(bxt_wcove_tmu_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Nilesh Bacchewar <nilesh.bacchewar@intel.com>"); +MODULE_DESCRIPTION("BXT Whiskey Cove TMU Driver"); diff --git a/arch/x86/platform/mellanox/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 7dcfcca97399..97b4c3a219c0 100644 --- a/arch/x86/platform/mellanox/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -1,5 +1,4 @@ /* - * arch/x86/platform/mellanox/mlx-platform.c * Copyright (c) 2016 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Vadim Pasternak <vadimp@mellanox.com> * @@ -39,6 +38,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/platform_data/i2c-mux-reg.h> +#include <linux/platform_data/mlxcpld-hotplug.h> #define MLX_PLAT_DEVICE_NAME "mlxplat" @@ -70,6 +70,7 @@ struct mlxplat_priv { struct platform_device *pdev_i2c; struct platform_device *pdev_mux[MLXPLAT_CPLD_LPC_MUX_DEVS]; + struct platform_device *pdev_hotplug; }; /* Regions for LPC I2C controller and LPC base register space */ @@ -121,7 +122,87 @@ static struct i2c_mux_reg_platform_data mlxplat_mux_data[] = { }; -static struct platform_device *mlxplat_dev; +/* Platform hotplug devices */ +static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_psu[] = { + { + .brdinfo = { I2C_BOARD_INFO("24c02", 0x51) }, + .bus = 10, + }, + { + .brdinfo = { I2C_BOARD_INFO("24c02", 0x50) }, + .bus = 10, + }, +}; + +static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_pwr[] = { + { + .brdinfo = { I2C_BOARD_INFO("dps460", 0x59) }, + .bus = 10, + }, + { + .brdinfo = { I2C_BOARD_INFO("dps460", 0x58) }, + .bus = 10, + }, +}; + +static struct mlxcpld_hotplug_device mlxplat_mlxcpld_hotplug_fan[] = { + { + .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, + .bus = 11, + }, + { + .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, + .bus = 12, + }, + { + .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, + .bus = 13, + }, + { + .brdinfo = { I2C_BOARD_INFO("24c32", 0x50) }, + .bus = 14, + }, +}; + +/* Platform hotplug default data */ +static +struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_default_data = { + .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a), + .top_aggr_mask = 0x48, + .top_aggr_psu_mask = 0x08, + .psu_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x58), + .psu_mask = 0x03, + .psu_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_psu), + .psu = mlxplat_mlxcpld_hotplug_psu, + .top_aggr_pwr_mask = 0x08, + .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64), + .pwr_mask = 0x03, + .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr), + .pwr = mlxplat_mlxcpld_hotplug_pwr, + .top_aggr_fan_mask = 0x40, + .fan_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x88), + .fan_mask = 0x0f, + .fan_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_fan), + .fan = mlxplat_mlxcpld_hotplug_fan, +}; + +/* Platform hotplug MSN21xx system family data */ +static +struct mlxcpld_hotplug_platform_data mlxplat_mlxcpld_hotplug_msn21xx_data = { + .top_aggr_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x3a), + .top_aggr_mask = 0x04, + .top_aggr_pwr_mask = 0x04, + .pwr_reg_offset = (MLXPLAT_CPLD_LPC_REG_BASE_ADRR | 0x64), + .pwr_mask = 0x03, + .pwr_count = ARRAY_SIZE(mlxplat_mlxcpld_hotplug_pwr), +}; + +static struct resource mlxplat_mlxcpld_hotplug_resources[] = { + [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"), +}; + +struct platform_device *mlxplat_dev; +struct mlxcpld_hotplug_platform_data *mlxplat_hotplug; static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) { @@ -132,6 +213,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) mlxplat_mux_data[i].n_values = ARRAY_SIZE(mlxplat_default_channels[i]); } + mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_default_data; return 1; }; @@ -145,6 +227,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) mlxplat_mux_data[i].n_values = ARRAY_SIZE(mlxplat_msn21xx_channels); } + mlxplat_hotplug = &mlxplat_mlxcpld_hotplug_msn21xx_data; return 1; }; @@ -216,7 +299,7 @@ static int __init mlxplat_init(void) if (IS_ERR(priv->pdev_i2c)) { err = PTR_ERR(priv->pdev_i2c); goto fail_alloc; - }; + } for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) { priv->pdev_mux[i] = platform_device_register_resndata( @@ -230,6 +313,16 @@ static int __init mlxplat_init(void) } } + priv->pdev_hotplug = platform_device_register_resndata( + &mlxplat_dev->dev, "mlxcpld-hotplug", -1, + mlxplat_mlxcpld_hotplug_resources, + ARRAY_SIZE(mlxplat_mlxcpld_hotplug_resources), + mlxplat_hotplug, sizeof(*mlxplat_hotplug)); + if (IS_ERR(priv->pdev_hotplug)) { + err = PTR_ERR(priv->pdev_hotplug); + goto fail_platform_mux_register; + } + return 0; fail_platform_mux_register: @@ -248,6 +341,8 @@ static void __exit mlxplat_exit(void) struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); int i; + platform_device_unregister(priv->pdev_hotplug); + for (i = ARRAY_SIZE(mlxplat_mux_data) - 1; i >= 0 ; i--) platform_device_unregister(priv->pdev_mux[i]); diff --git a/drivers/platform/x86/surface3-wmi.c b/drivers/platform/x86/surface3-wmi.c new file mode 100644 index 000000000000..cbf4d83a7271 --- /dev/null +++ b/drivers/platform/x86/surface3-wmi.c @@ -0,0 +1,297 @@ +/* + * Driver for the LID cover switch of the Surface 3 + * + * Copyright (c) 2016 Red Hat Inc. + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/input.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> + +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@redhat.com>"); +MODULE_DESCRIPTION("Surface 3 platform driver"); +MODULE_LICENSE("GPL"); + +#define ACPI_BUTTON_HID_LID "PNP0C0D" +#define SPI_CTL_OBJ_NAME "SPI" +#define SPI_TS_OBJ_NAME "NTRG" + +#define SURFACE3_LID_GUID "F7CC25EC-D20B-404C-8903-0ED4359C18AE" + +MODULE_ALIAS("wmi:" SURFACE3_LID_GUID); + +static const struct dmi_system_id surface3_dmi_table[] = { +#if defined(CONFIG_X86) + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), + }, + }, +#endif + { } +}; + +struct surface3_wmi { + struct acpi_device *touchscreen_adev; + struct acpi_device *pnp0c0d_adev; + struct acpi_hotplug_context hp; + struct input_dev *input; +}; + +static struct platform_device *s3_wmi_pdev; + +static struct surface3_wmi s3_wmi; + +static DEFINE_MUTEX(s3_wmi_lock); + +static int s3_wmi_query_block(const char *guid, int instance, int *ret) +{ + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + union acpi_object *obj; + int error = 0; + + mutex_lock(&s3_wmi_lock); + status = wmi_query_block(guid, instance, &output); + + obj = output.pointer; + + if (!obj || obj->type != ACPI_TYPE_INTEGER) { + if (obj) { + pr_err("query block returned object type: %d - buffer length:%d\n", + obj->type, + obj->type == ACPI_TYPE_BUFFER ? + obj->buffer.length : 0); + } + error = -EINVAL; + goto out_free_unlock; + } + *ret = obj->integer.value; + out_free_unlock: + kfree(obj); + mutex_unlock(&s3_wmi_lock); + return error; +} + +static inline int s3_wmi_query_lid(int *ret) +{ + return s3_wmi_query_block(SURFACE3_LID_GUID, 0, ret); +} + +static int s3_wmi_send_lid_state(void) +{ + int ret, lid_sw; + + ret = s3_wmi_query_lid(&lid_sw); + if (ret) + return ret; + + input_report_switch(s3_wmi.input, SW_LID, lid_sw); + input_sync(s3_wmi.input); + + return 0; +} + +static int s3_wmi_hp_notify(struct acpi_device *adev, u32 value) +{ + return s3_wmi_send_lid_state(); +} + +static acpi_status s3_wmi_attach_spi_device(acpi_handle handle, + u32 level, + void *data, + void **return_value) +{ + struct acpi_device *adev, **ts_adev; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + ts_adev = data; + + if (strncmp(acpi_device_bid(adev), SPI_TS_OBJ_NAME, + strlen(SPI_TS_OBJ_NAME))) + return AE_OK; + + if (*ts_adev) { + pr_err("duplicate entry %s\n", SPI_TS_OBJ_NAME); + return AE_OK; + } + + *ts_adev = adev; + + return AE_OK; +} + +static int s3_wmi_check_platform_device(struct device *dev, void *data) +{ + struct acpi_device *adev, *ts_adev; + acpi_handle handle; + acpi_status status; + + /* ignore non ACPI devices */ + handle = ACPI_HANDLE(dev); + if (!handle || acpi_bus_get_device(handle, &adev)) + return 0; + + /* check for LID ACPI switch */ + if (!strcmp(ACPI_BUTTON_HID_LID, acpi_device_hid(adev))) { + s3_wmi.pnp0c0d_adev = adev; + return 0; + } + + /* ignore non SPI controllers */ + if (strncmp(acpi_device_bid(adev), SPI_CTL_OBJ_NAME, + strlen(SPI_CTL_OBJ_NAME))) + return 0; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + s3_wmi_attach_spi_device, NULL, + &ts_adev, NULL); + if (ACPI_FAILURE(status)) + dev_warn(dev, "failed to enumerate SPI slaves\n"); + + if (!ts_adev) + return 0; + + s3_wmi.touchscreen_adev = ts_adev; + + return 0; +} + +static int s3_wmi_create_and_register_input(struct platform_device *pdev) +{ + struct input_dev *input; + int error; + + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; + + input->name = "Lid Switch"; + input->phys = "button/input0"; + input->id.bustype = BUS_HOST; + input->id.product = 0x0005; + + input_set_capability(input, EV_SW, SW_LID); + + error = input_register_device(input); + if (error) + goto out_err; + + s3_wmi.input = input; + + return 0; + out_err: + input_free_device(s3_wmi.input); + return error; +} + +static int __init s3_wmi_probe(struct platform_device *pdev) +{ + int error; + + if (!dmi_check_system(surface3_dmi_table)) + return -ENODEV; + + memset(&s3_wmi, 0, sizeof(s3_wmi)); + + bus_for_each_dev(&platform_bus_type, NULL, NULL, + s3_wmi_check_platform_device); + + if (!s3_wmi.touchscreen_adev) + return -ENODEV; + + acpi_bus_trim(s3_wmi.pnp0c0d_adev); + + error = s3_wmi_create_and_register_input(pdev); + if (error) + goto restore_acpi_lid; + + acpi_initialize_hp_context(s3_wmi.touchscreen_adev, &s3_wmi.hp, + s3_wmi_hp_notify, NULL); + + s3_wmi_send_lid_state(); + + return 0; + + restore_acpi_lid: + acpi_bus_scan(s3_wmi.pnp0c0d_adev->handle); + return error; +} + +static int s3_wmi_remove(struct platform_device *device) +{ + /* remove the hotplug context from the acpi device */ + s3_wmi.touchscreen_adev->hp = NULL; + + /* reinstall the actual PNPC0C0D LID default handle */ + acpi_bus_scan(s3_wmi.pnp0c0d_adev->handle); + return 0; +} + +#ifdef CONFIG_PM +static int s3_wmi_resume(struct device *dev) +{ + s3_wmi_send_lid_state(); + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(s3_wmi_pm, NULL, s3_wmi_resume); + +static struct platform_driver s3_wmi_driver = { + .driver = { + .name = "surface3-wmi", + .pm = &s3_wmi_pm, + }, + .remove = s3_wmi_remove, +}; + +static int __init s3_wmi_init(void) +{ + int error; + + s3_wmi_pdev = platform_device_alloc("surface3-wmi", -1); + if (!s3_wmi_pdev) + return -ENOMEM; + + error = platform_device_add(s3_wmi_pdev); + if (error) + goto err_device_put; + + error = platform_driver_probe(&s3_wmi_driver, s3_wmi_probe); + if (error) + goto err_device_del; + + pr_info("Surface 3 WMI Extras loaded\n"); + return 0; + + err_device_del: + platform_device_del(s3_wmi_pdev); + err_device_put: + platform_device_put(s3_wmi_pdev); + return error; +} + +static void __exit s3_wmi_exit(void) +{ + platform_device_unregister(s3_wmi_pdev); + platform_driver_unregister(&s3_wmi_driver); +} + +module_init(s3_wmi_init); +module_exit(s3_wmi_exit); diff --git a/drivers/platform/x86/surface3_button.c b/drivers/platform/x86/surface3_button.c new file mode 100644 index 000000000000..8bfd7f613d36 --- /dev/null +++ b/drivers/platform/x86/surface3_button.c @@ -0,0 +1,250 @@ +/* + * Supports for the button array on the Surface tablets. + * + * (C) Copyright 2016 Red Hat, Inc + * + * Based on soc_button_array.c: + * + * {C} Copyright 2014 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/acpi.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio_keys.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> + + +#define SURFACE_BUTTON_OBJ_NAME "TEV2" +#define MAX_NBUTTONS 4 + +/* + * Some of the buttons like volume up/down are auto repeat, while others + * are not. To support both, we register two platform devices, and put + * buttons into them based on whether the key should be auto repeat. + */ +#define BUTTON_TYPES 2 + +/* + * Power button, Home button, Volume buttons support is supposed to + * be covered by drivers/input/misc/soc_button_array.c, which is implemented + * according to "Windows ACPI Design Guide for SoC Platforms". + * However surface 3 seems not to obey the specs, instead it uses + * device TEV2(MSHW0028) for declaring the GPIOs. The gpios are also slightly + * different in which the Home button is active high. + * Compared to surfacepro3_button.c which also handles MSHW0028, the Surface 3 + * is a reduce platform and thus uses GPIOs, not ACPI events. + * We choose an I2C driver here because we need to access the resources + * declared under the device node, while surfacepro3_button.c only needs + * the ACPI companion node. + */ +static const struct acpi_device_id surface3_acpi_match[] = { + { "MSHW0028", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, surface3_acpi_match); + +struct surface3_button_info { + const char *name; + int acpi_index; + unsigned int event_type; + unsigned int event_code; + bool autorepeat; + bool wakeup; + bool active_low; +}; + +struct surface3_button_data { + struct platform_device *children[BUTTON_TYPES]; +}; + +/* + * Get the Nth GPIO number from the ACPI object. + */ +static int surface3_button_lookup_gpio(struct device *dev, int acpi_index) +{ + struct gpio_desc *desc; + int gpio; + + desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + gpio = desc_to_gpio(desc); + + gpiod_put(desc); + + return gpio; +} + +static struct platform_device * +surface3_button_device_create(struct i2c_client *client, + const struct surface3_button_info *button_info, + bool autorepeat) +{ + const struct surface3_button_info *info; + struct platform_device *pd; + struct gpio_keys_button *gpio_keys; + struct gpio_keys_platform_data *gpio_keys_pdata; + int n_buttons = 0; + int gpio; + int error; + + gpio_keys_pdata = devm_kzalloc(&client->dev, + sizeof(*gpio_keys_pdata) + + sizeof(*gpio_keys) * MAX_NBUTTONS, + GFP_KERNEL); + if (!gpio_keys_pdata) + return ERR_PTR(-ENOMEM); + + gpio_keys = (void *)(gpio_keys_pdata + 1); + + for (info = button_info; info->name; info++) { + if (info->autorepeat != autorepeat) + continue; + + gpio = surface3_button_lookup_gpio(&client->dev, + info->acpi_index); + if (!gpio_is_valid(gpio)) + continue; + + gpio_keys[n_buttons].type = info->event_type; + gpio_keys[n_buttons].code = info->event_code; + gpio_keys[n_buttons].gpio = gpio; + gpio_keys[n_buttons].active_low = info->active_low; + gpio_keys[n_buttons].desc = info->name; + gpio_keys[n_buttons].wakeup = info->wakeup; + n_buttons++; + } + + if (n_buttons == 0) { + error = -ENODEV; + goto err_free_mem; + } + + gpio_keys_pdata->buttons = gpio_keys; + gpio_keys_pdata->nbuttons = n_buttons; + gpio_keys_pdata->rep = autorepeat; + + pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO); + if (!pd) { + error = -ENOMEM; + goto err_free_mem; + } + + error = platform_device_add_data(pd, gpio_keys_pdata, + sizeof(*gpio_keys_pdata)); + if (error) + goto err_free_pdev; + + error = platform_device_add(pd); + if (error) + goto err_free_pdev; + + return pd; + +err_free_pdev: + platform_device_put(pd); +err_free_mem: + devm_kfree(&client->dev, gpio_keys_pdata); + return ERR_PTR(error); +} + +static int surface3_button_remove(struct i2c_client *client) +{ + struct surface3_button_data *priv = i2c_get_clientdata(client); + + int i; + + for (i = 0; i < BUTTON_TYPES; i++) + if (priv->children[i]) + platform_device_unregister(priv->children[i]); + + return 0; +} + +static struct surface3_button_info surface3_button_surface3[] = { + { "power", 0, EV_KEY, KEY_POWER, false, true, true }, + { "home", 1, EV_KEY, KEY_LEFTMETA, false, true, false }, + { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true }, + { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true }, + { } +}; + +static int surface3_button_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct surface3_button_data *priv; + struct platform_device *pd; + int i; + int error; + + if (strncmp(acpi_device_bid(ACPI_COMPANION(&client->dev)), + SURFACE_BUTTON_OBJ_NAME, + strlen(SURFACE_BUTTON_OBJ_NAME))) + return -ENODEV; + + if (gpiod_count(dev, KBUILD_MODNAME) <= 0) { + dev_dbg(dev, "no GPIO attached, ignoring...\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_clientdata(client, priv); + + for (i = 0; i < BUTTON_TYPES; i++) { + pd = surface3_button_device_create(client, + surface3_button_surface3, + i == 0); + if (IS_ERR(pd)) { + error = PTR_ERR(pd); + if (error != -ENODEV) { + surface3_button_remove(client); + return error; + } + continue; + } + + priv->children[i] = pd; + } + + if (!priv->children[0] && !priv->children[1]) + return -ENODEV; + + return 0; +} + +static const struct i2c_device_id surface3_id[] = { + { } +}; +MODULE_DEVICE_TABLE(i2c, surface3_id); + +static struct i2c_driver surface3_driver = { + .probe = surface3_button_probe, + .remove = surface3_button_remove, + .id_table = surface3_id, + .driver = { + .name = "surface3", + .acpi_match_table = ACPI_PTR(surface3_acpi_match), + }, +}; +module_i2c_driver(surface3_driver); + +MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>"); +MODULE_DESCRIPTION("surface3 button array driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/intel_soc_pmic.h b/include/linux/mfd/intel_soc_pmic.h index cf619dbeace2..956caa0628f5 100644 --- a/include/linux/mfd/intel_soc_pmic.h +++ b/include/linux/mfd/intel_soc_pmic.h @@ -26,6 +26,7 @@ struct intel_soc_pmic { struct regmap *regmap; struct regmap_irq_chip_data *irq_chip_data; struct regmap_irq_chip_data *irq_chip_data_level2; + struct regmap_irq_chip_data *irq_chip_data_tmu; struct device *dev; }; |