88f3771
From c0f9254fdd0703ade018b2ff3a8cca433f781a11 Mon Sep 17 00:00:00 2001
88f3771
From: Hans de Goede <hdegoede@redhat.com>
88f3771
Date: Sun, 26 Feb 2017 21:07:29 +0100
88f3771
Subject: [PATCH 02/16] mfd: Add Cherry Trail Whiskey Cove PMIC driver
88f3771
88f3771
Add mfd driver for Intel CHT Whiskey Cove PMIC, based on various non
88f3771
upstreamed CHT Whiskey Cove PMIC patches.
88f3771
88f3771
This is a somewhat minimal version which adds irqchip support and cells
88f3771
for: ACPI PMIC opregion support, the i2c-controller driving the external
88f3771
charger irc and the pwrsrc/extcon block.
88f3771
88f3771
Further cells can be added in the future if/when drivers are upstreamed
88f3771
for them.
88f3771
88f3771
Cc: Bin Gao <bin.gao@intel.com>
88f3771
Cc: Felipe Balbi <felipe.balbi@linux.intel.com>
88f3771
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
88f3771
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
88f3771
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
88f3771
---
88f3771
Changes in v2:
88f3771
-Since this uses plain mfd and not the intel_soc_pmic stuff give it
88f3771
 its own Kconfig and allow this to be built as a module
88f3771
-Add missing #include <acpi/acpi_bus.h>
88f3771
88f3771
Changes in v3:
88f3771
-Drop #include <acpi/acpi_bus.h> again, not the right fix for the build errors
88f3771
-Error out when the upper byte of the register-address passed to the regmap
88f3771
 functions is 0 rather then hardcoding an address in that case
88f3771
-Various minor style tweaks / cleanups
88f3771
-Move defines of regulator register addresses to intel_pmic_chtwc.c,
88f3771
 it is the only place where they are used
88f3771
-Drop now empty include/linux/mfd/intel_chtwc.h
88f3771
-Rename intel_soc_pmic_chtwc.c to intel_cht_wc.c to match Kconfig option name
88f3771
-Add irqchip support
88f3771
-Add external charger cell
88f3771
-Add pwrsrc cell
88f3771
88f3771
Changes in v4:
88f3771
-Use PLATFORM_DEVID_NONE
88f3771
88f3771
Changes in v5:
88f3771
-Change Kconfig option from tristate to boolean and add a select for the
88f3771
 i2c-bus driver, this is necessary because the chtwc PMIC provides an ACPI
88f3771
 OPRegion handler, which must be available before other drivers using it
88f3771
 are loaded, which can only be ensured if the mfd, opregion and i2c-bus
88f3771
 drivers are built in. This fixes errors like these during boot:
88f3771
 mmc0: SDHCI controller on ACPI [80860F14:00] using ADMA
88f3771
 ACPI Error: No handler for Region [REGS] (ffff93543b0cc3a8) [UserDefinedRegion] (20170119/evregion-166)
88f3771
 ACPI Error: Region UserDefinedRegion (ID=143) has no handler (20170119/exfldio-299)
88f3771
 ACPI Error: Method parse/execution failed [\_SB.PCI0.I2C7.PMI5.GET] (Node ffff93543b0cde10), AE_NOT_EXIST (20170119/psparse-543)
88f3771
 ACPI Error: Method parse/execution failed [\_SB.PCI0.SHC1._PS0] (Node ffff93543b0b5cd0), AE_NOT_EXIST (20170119/psparse-543)
88f3771
 acpi 80860F14:02: Failed to change power state to D0
88f3771
-Some minor style and capitalization fixes from review by Lee Jones
88f3771
88f3771
Changes in v6:
88f3771
-Fix Kconfig depends and selects to fix warning reported by kbuild test robot
88f3771
88f3771
Changes in v7:
88f3771
-Add explanation why this is a bool and why it selects i2c-designwaree
88f3771
 to the help text rather then as comments in the Kconfig
88f3771
88f3771
Changes in v8:
88f3771
-Remove MODULE macros, etc. now that this driver is a bool in Kconfig
88f3771
88f3771
Changes in v9:
88f3771
-Some whitespace tweaks
88f3771
-Return -EINVAL from probe on invalid irq
88f3771
-Use probe_new i2c_driver callback
88f3771
---
88f3771
 drivers/mfd/Kconfig                |  16 +++
88f3771
 drivers/mfd/Makefile               |   1 +
88f3771
 drivers/mfd/intel_soc_pmic_chtwc.c | 230 +++++++++++++++++++++++++++++++++++++
88f3771
 3 files changed, 247 insertions(+)
88f3771
 create mode 100644 drivers/mfd/intel_soc_pmic_chtwc.c
88f3771
88f3771
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
88f3771
index 3eb5c93595f6..5203a86b8f6c 100644
88f3771
--- a/drivers/mfd/Kconfig
88f3771
+++ b/drivers/mfd/Kconfig
88f3771
@@ -470,6 +470,22 @@ config INTEL_SOC_PMIC_BXTWC
88f3771
 	  thermal, charger and related power management functions
88f3771
 	  on these systems.
88f3771
 
88f3771
+config INTEL_SOC_PMIC_CHTWC
88f3771
+	bool "Support for Intel Cherry Trail Whiskey Cove PMIC"
88f3771
+	depends on ACPI && HAS_IOMEM && I2C=y && COMMON_CLK
88f3771
+	depends on X86 || COMPILE_TEST
88f3771
+	select MFD_CORE
88f3771
+	select REGMAP_I2C
88f3771
+	select REGMAP_IRQ
88f3771
+	select I2C_DESIGNWARE_PLATFORM
88f3771
+	help
88f3771
+	  Select this option to enable support for the Intel Cherry Trail
88f3771
+	  Whiskey Cove PMIC found on some Intel Cherry Trail systems.
88f3771
+
88f3771
+	  This option is a bool as it provides an ACPI OpRegion which must be
88f3771
+	  available before any devices using it are probed. This option also
88f3771
+	  causes the designware-i2c driver to be builtin for the same reason.
88f3771
+
88f3771
 config MFD_INTEL_LPSS
88f3771
 	tristate
88f3771
 	select COMMON_CLK
88f3771
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
88f3771
index c16bf1ea0ea9..6f6aed8cfccc 100644
88f3771
--- a/drivers/mfd/Makefile
88f3771
+++ b/drivers/mfd/Makefile
88f3771
@@ -214,6 +214,7 @@ obj-$(CONFIG_MFD_SKY81452)	+= sky81452.o
88f3771
 intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
88f3771
 obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
88f3771
 obj-$(CONFIG_INTEL_SOC_PMIC_BXTWC)	+= intel_soc_pmic_bxtwc.o
88f3771
+obj-$(CONFIG_INTEL_SOC_PMIC_CHTWC)	+= intel_soc_pmic_chtwc.o
88f3771
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
88f3771
 
88f3771
 obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
88f3771
diff --git a/drivers/mfd/intel_soc_pmic_chtwc.c b/drivers/mfd/intel_soc_pmic_chtwc.c
88f3771
new file mode 100644
88f3771
index 000000000000..b35da01d5bcf
88f3771
--- /dev/null
88f3771
+++ b/drivers/mfd/intel_soc_pmic_chtwc.c
88f3771
@@ -0,0 +1,230 @@
88f3771
+/*
88f3771
+ * MFD core driver for Intel Cherrytrail Whiskey Cove PMIC
88f3771
+ *
88f3771
+ * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
88f3771
+ *
88f3771
+ * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
88f3771
+ * Copyright (C) 2013-2015 Intel Corporation. All rights reserved.
88f3771
+ *
88f3771
+ * This program is free software; you can redistribute it and/or modify
88f3771
+ * it under the terms of the GNU General Public License version 2 as
88f3771
+ * published by the Free Software Foundation.
88f3771
+ */
88f3771
+
88f3771
+#include <linux/acpi.h>
88f3771
+#include <linux/delay.h>
88f3771
+#include <linux/err.h>
88f3771
+#include <linux/i2c.h>
88f3771
+#include <linux/interrupt.h>
88f3771
+#include <linux/kernel.h>
88f3771
+#include <linux/mfd/core.h>
88f3771
+#include <linux/mfd/intel_soc_pmic.h>
88f3771
+#include <linux/regmap.h>
88f3771
+
88f3771
+/* PMIC device registers */
88f3771
+#define REG_OFFSET_MASK		GENMASK(7, 0)
88f3771
+#define REG_ADDR_MASK		GENMASK(15, 8)
88f3771
+#define REG_ADDR_SHIFT		8
88f3771
+
88f3771
+#define CHT_WC_IRQLVL1		0x6e02
88f3771
+#define CHT_WC_IRQLVL1_MASK	0x6e0e
88f3771
+
88f3771
+/* Whiskey Cove PMIC share same ACPI ID between different platforms */
88f3771
+#define CHT_WC_HRV		3
88f3771
+
88f3771
+/* Level 1 IRQs (level 2 IRQs are handled in the child device drivers) */
88f3771
+enum {
88f3771
+	CHT_WC_PWRSRC_IRQ = 0,
88f3771
+	CHT_WC_THRM_IRQ,
88f3771
+	CHT_WC_BCU_IRQ,
88f3771
+	CHT_WC_ADC_IRQ,
88f3771
+	CHT_WC_EXT_CHGR_IRQ,
88f3771
+	CHT_WC_GPIO_IRQ,
88f3771
+	/* There is no irq 6 */
88f3771
+	CHT_WC_CRIT_IRQ = 7,
88f3771
+};
88f3771
+
88f3771
+static struct resource cht_wc_pwrsrc_resources[] = {
88f3771
+	DEFINE_RES_IRQ(CHT_WC_PWRSRC_IRQ),
88f3771
+};
88f3771
+
88f3771
+static struct resource cht_wc_ext_charger_resources[] = {
88f3771
+	DEFINE_RES_IRQ(CHT_WC_EXT_CHGR_IRQ),
88f3771
+};
88f3771
+
88f3771
+static struct mfd_cell cht_wc_dev[] = {
88f3771
+	{
88f3771
+		.name = "cht_wcove_pwrsrc",
88f3771
+		.num_resources = ARRAY_SIZE(cht_wc_pwrsrc_resources),
88f3771
+		.resources = cht_wc_pwrsrc_resources,
88f3771
+	}, {
88f3771
+		.name = "cht_wcove_ext_chgr",
88f3771
+		.num_resources = ARRAY_SIZE(cht_wc_ext_charger_resources),
88f3771
+		.resources = cht_wc_ext_charger_resources,
88f3771
+	},
88f3771
+	{	.name = "cht_wcove_region", },
88f3771
+};
88f3771
+
88f3771
+/*
88f3771
+ * The CHT Whiskey Cove covers multiple I2C addresses, with a 1 Byte
88f3771
+ * register address space per I2C address, so we use 16 bit register
88f3771
+ * addresses where the high 8 bits contain the I2C client address.
88f3771
+ */
88f3771
+static int cht_wc_byte_reg_read(void *context, unsigned int reg,
88f3771
+				unsigned int *val)
88f3771
+{
88f3771
+	struct i2c_client *client = context;
88f3771
+	int ret, orig_addr = client->addr;
88f3771
+
88f3771
+	if (!(reg & REG_ADDR_MASK)) {
88f3771
+		dev_err(&client->dev, "Error I2C address not specified\n");
88f3771
+		return -EINVAL;
88f3771
+	}
88f3771
+
88f3771
+	client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT;
88f3771
+	ret = i2c_smbus_read_byte_data(client, reg & REG_OFFSET_MASK);
88f3771
+	client->addr = orig_addr;
88f3771
+
88f3771
+	if (ret < 0)
88f3771
+		return ret;
88f3771
+
88f3771
+	*val = ret;
88f3771
+	return 0;
88f3771
+}
88f3771
+
88f3771
+static int cht_wc_byte_reg_write(void *context, unsigned int reg,
88f3771
+				 unsigned int val)
88f3771
+{
88f3771
+	struct i2c_client *client = context;
88f3771
+	int ret, orig_addr = client->addr;
88f3771
+
88f3771
+	if (!(reg & REG_ADDR_MASK)) {
88f3771
+		dev_err(&client->dev, "Error I2C address not specified\n");
88f3771
+		return -EINVAL;
88f3771
+	}
88f3771
+
88f3771
+	client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT;
88f3771
+	ret = i2c_smbus_write_byte_data(client, reg & REG_OFFSET_MASK, val);
88f3771
+	client->addr = orig_addr;
88f3771
+
88f3771
+	return ret;
88f3771
+}
88f3771
+
88f3771
+static const struct regmap_config cht_wc_regmap_cfg = {
88f3771
+	.reg_bits = 16,
88f3771
+	.val_bits = 8,
88f3771
+	.reg_write = cht_wc_byte_reg_write,
88f3771
+	.reg_read = cht_wc_byte_reg_read,
88f3771
+};
88f3771
+
88f3771
+static const struct regmap_irq cht_wc_regmap_irqs[] = {
88f3771
+	REGMAP_IRQ_REG(CHT_WC_PWRSRC_IRQ, 0, BIT(CHT_WC_PWRSRC_IRQ)),
88f3771
+	REGMAP_IRQ_REG(CHT_WC_THRM_IRQ, 0, BIT(CHT_WC_THRM_IRQ)),
88f3771
+	REGMAP_IRQ_REG(CHT_WC_BCU_IRQ, 0, BIT(CHT_WC_BCU_IRQ)),
88f3771
+	REGMAP_IRQ_REG(CHT_WC_ADC_IRQ, 0, BIT(CHT_WC_ADC_IRQ)),
88f3771
+	REGMAP_IRQ_REG(CHT_WC_EXT_CHGR_IRQ, 0, BIT(CHT_WC_EXT_CHGR_IRQ)),
88f3771
+	REGMAP_IRQ_REG(CHT_WC_GPIO_IRQ, 0, BIT(CHT_WC_GPIO_IRQ)),
88f3771
+	REGMAP_IRQ_REG(CHT_WC_CRIT_IRQ, 0, BIT(CHT_WC_CRIT_IRQ)),
88f3771
+};
88f3771
+
88f3771
+static const struct regmap_irq_chip cht_wc_regmap_irq_chip = {
88f3771
+	.name = "cht_wc_irq_chip",
88f3771
+	.status_base = CHT_WC_IRQLVL1,
88f3771
+	.mask_base = CHT_WC_IRQLVL1_MASK,
88f3771
+	.irqs = cht_wc_regmap_irqs,
88f3771
+	.num_irqs = ARRAY_SIZE(cht_wc_regmap_irqs),
88f3771
+	.num_regs = 1,
88f3771
+};
88f3771
+
88f3771
+static int cht_wc_probe(struct i2c_client *client)
88f3771
+{
88f3771
+	struct device *dev = &client->dev;
88f3771
+	struct intel_soc_pmic *pmic;
88f3771
+	acpi_status status;
88f3771
+	unsigned long long hrv;
88f3771
+	int ret;
88f3771
+
88f3771
+	status = acpi_evaluate_integer(ACPI_HANDLE(dev), "_HRV", NULL, &hrv);
88f3771
+	if (ACPI_FAILURE(status)) {
88f3771
+		dev_err(dev, "Failed to get PMIC hardware revision\n");
88f3771
+		return -ENODEV;
88f3771
+	}
88f3771
+	if (hrv != CHT_WC_HRV) {
88f3771
+		dev_err(dev, "Invalid PMIC hardware revision: %llu\n", hrv);
88f3771
+		return -ENODEV;
88f3771
+	}
88f3771
+	if (client->irq < 0) {
88f3771
+		dev_err(dev, "Invalid IRQ\n");
88f3771
+		return -EINVAL;
88f3771
+	}
88f3771
+
88f3771
+	pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
88f3771
+	if (!pmic)
88f3771
+		return -ENOMEM;
88f3771
+
88f3771
+	pmic->irq = client->irq;
88f3771
+	pmic->dev = dev;
88f3771
+	i2c_set_clientdata(client, pmic);
88f3771
+
88f3771
+	pmic->regmap = devm_regmap_init(dev, NULL, client, &cht_wc_regmap_cfg);
88f3771
+	if (IS_ERR(pmic->regmap))
88f3771
+		return PTR_ERR(pmic->regmap);
88f3771
+
88f3771
+	ret = devm_regmap_add_irq_chip(dev, pmic->regmap, pmic->irq,
88f3771
+				       IRQF_ONESHOT | IRQF_SHARED, 0,
88f3771
+				       &cht_wc_regmap_irq_chip,
88f3771
+				       &pmic->irq_chip_data);
88f3771
+	if (ret)
88f3771
+		return ret;
88f3771
+
88f3771
+	return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
88f3771
+				cht_wc_dev, ARRAY_SIZE(cht_wc_dev), NULL, 0,
88f3771
+				regmap_irq_get_domain(pmic->irq_chip_data));
88f3771
+}
88f3771
+
88f3771
+static void cht_wc_shutdown(struct i2c_client *client)
88f3771
+{
88f3771
+	struct intel_soc_pmic *pmic = i2c_get_clientdata(client);
88f3771
+
88f3771
+	disable_irq(pmic->irq);
88f3771
+}
88f3771
+
88f3771
+static int __maybe_unused cht_wc_suspend(struct device *dev)
88f3771
+{
88f3771
+	struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
88f3771
+
88f3771
+	disable_irq(pmic->irq);
88f3771
+
88f3771
+	return 0;
88f3771
+}
88f3771
+
88f3771
+static int __maybe_unused cht_wc_resume(struct device *dev)
88f3771
+{
88f3771
+	struct intel_soc_pmic *pmic = dev_get_drvdata(dev);
88f3771
+
88f3771
+	enable_irq(pmic->irq);
88f3771
+
88f3771
+	return 0;
88f3771
+}
88f3771
+static SIMPLE_DEV_PM_OPS(cht_wc_pm_ops, cht_wc_suspend, cht_wc_resume);
88f3771
+
88f3771
+static const struct i2c_device_id cht_wc_i2c_id[] = {
88f3771
+	{ }
88f3771
+};
88f3771
+
88f3771
+static const struct acpi_device_id cht_wc_acpi_ids[] = {
88f3771
+	{ "INT34D3", },
88f3771
+	{ }
88f3771
+};
88f3771
+
88f3771
+static struct i2c_driver cht_wc_driver = {
88f3771
+	.driver	= {
88f3771
+		.name	= "CHT Whiskey Cove PMIC",
88f3771
+		.pm     = &cht_wc_pm_ops,
88f3771
+		.acpi_match_table = cht_wc_acpi_ids,
88f3771
+	},
88f3771
+	.probe_new = cht_wc_probe,
88f3771
+	.shutdown = cht_wc_shutdown,
88f3771
+	.id_table = cht_wc_i2c_id,
88f3771
+};
88f3771
+builtin_i2c_driver(cht_wc_driver);
88f3771
-- 
88f3771
2.13.0
88f3771