[PATCH v2] mfd: Add Cherrytrail WhiskeyCove PMIC driver
From: Hans de Goede
Date: Mon Feb 27 2017 - 17:56:27 EST
Add mfd driver for Intel CHT WhiskeyCove PMIC, based on various non
upstreamed CHT WhiskeyCove PMIC patches. For now this just adds a minimal
version which implements just enough to get ACPI PMIC opregion support to
work, so that suspend/resume will work on machines with this PMIC.
Cc: Bin Gao <bin.gao@xxxxxxxxx>
Cc: Felipe Balbi <felipe.balbi@xxxxxxxxxxxxxxx>
Cc: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx>
Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx>
---
Changes in v2:
-Since this uses plain mfd and not the intel_soc_pmic stuff give it
its own Kconfig and allow this to be built as a module
-Add missing #include <acpi/acpi_bus.h>
---
drivers/mfd/Kconfig | 11 +++
drivers/mfd/Makefile | 1 +
drivers/mfd/intel_soc_pmic_chtwc.c | 176 +++++++++++++++++++++++++++++++++++++
include/linux/mfd/intel_chtwc.h | 75 ++++++++++++++++
4 files changed, 263 insertions(+)
create mode 100644 drivers/mfd/intel_soc_pmic_chtwc.c
create mode 100644 include/linux/mfd/intel_chtwc.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 4ce3b6f..98c38aa 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -436,6 +436,17 @@ config INTEL_SOC_PMIC
thermal, charger and related power management functions
on these systems.
+config MFD_INTEL_CHT_WC
+ tristate "Support for Intel Cherry Trail Whiskey Cove PMIC"
+ depends on ACPI
+ depends on I2C
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Select this option to enable support for the Intel Cherry Trail
+ Whiskey Cove PMIC found on some Intel Cherry Trail systems.
+
config MFD_INTEL_LPSS
tristate
select COMMON_CLK
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index dda4d4f..664cb81 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -204,6 +204,7 @@ obj-$(CONFIG_MFD_HI655X_PMIC) += hi655x-pmic.o
obj-$(CONFIG_MFD_DLN2) += dln2.o
obj-$(CONFIG_MFD_RT5033) += rt5033.o
obj-$(CONFIG_MFD_SKY81452) += sky81452.o
+obj-$(CONFIG_MFD_INTEL_CHT_WC) += intel_soc_pmic_chtwc.o
intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC) += intel_soc_pmic_bxtwc.o
diff --git a/drivers/mfd/intel_soc_pmic_chtwc.c b/drivers/mfd/intel_soc_pmic_chtwc.c
new file mode 100644
index 0000000..1d53da6
--- /dev/null
+++ b/drivers/mfd/intel_soc_pmic_chtwc.c
@@ -0,0 +1,176 @@
+/*
+ * MFD core driver for Intel Cherrytrail Whiskey Cove PMIC
+ * Copyright (C) 2017 Hans de Goede <hdegoede@xxxxxxxxxx>
+ *
+ * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
+ * Copyright (C) 2013-2015 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 <acpi/acpi_bus.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/intel_chtwc.h>
+#include <linux/mfd/intel_soc_pmic.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+/* PMIC device registers */
+#define REG_ADDR_MASK 0xff00
+#define REG_ADDR_SHIFT 8
+#define REG_OFFSET_MASK 0xff
+
+/* Whiskey Cove PMIC share same ACPI ID between different platforms */
+#define CHT_WC_HRV 3
+
+static struct mfd_cell cht_wc_dev[] = {
+ {
+ .name = "cht_wcove_region",
+ },
+};
+
+/*
+ * The CHT Whiskey Cove covers multiple i2c addresses, with a 1 byte
+ * register address space per i2c address, so we use 16 bit register
+ * addresses where the high 8 bits contain the i2c client address.
+ */
+static int cht_wc_byte_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct i2c_client *client = context;
+ int ret, orig_addr = client->addr;
+
+ if (reg & REG_ADDR_MASK)
+ client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT;
+ else
+ client->addr = CHT_WC_DEVICE1_ADDR;
+ ret = i2c_smbus_read_byte_data(client, reg & REG_OFFSET_MASK);
+ client->addr = orig_addr;
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return 0;
+}
+
+static int cht_wc_byte_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct i2c_client *client = context;
+ int ret, orig_addr = client->addr;
+
+ if (reg & REG_ADDR_MASK)
+ client->addr = (reg & REG_ADDR_MASK) >> REG_ADDR_SHIFT;
+ else
+ client->addr = CHT_WC_DEVICE1_ADDR;
+ ret = i2c_smbus_write_byte_data(client, reg & REG_OFFSET_MASK, val);
+ client->addr = orig_addr;
+
+ return ret;
+}
+
+static const struct regmap_config cht_wc_regmap_cfg = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .reg_write = cht_wc_byte_reg_write,
+ .reg_read = cht_wc_byte_reg_read,
+};
+
+static int cht_wc_probe(struct i2c_client *client,
+ const struct i2c_device_id *i2c_id)
+{
+ struct device *dev = &client->dev;
+ struct intel_soc_pmic *pmic;
+ acpi_handle handle;
+ acpi_status status;
+ unsigned long long hrv;
+ int ret;
+
+ handle = ACPI_HANDLE(dev);
+ status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "Failed to get PMIC hardware revision\n");
+ return -ENODEV;
+ }
+ if (hrv != CHT_WC_HRV) {
+ dev_err(dev, "Invalid PMIC hardware revision: %llu\n", hrv);
+ return -ENODEV;
+ }
+ if (client->irq < 0) {
+ dev_err(dev, "Invalid IRQ\n");
+ return -ENODEV;
+ }
+
+ pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ pmic->irq = client->irq;
+ pmic->dev = dev;
+ i2c_set_clientdata(client, pmic);
+
+ pmic->regmap = devm_regmap_init(dev, NULL, client, &cht_wc_regmap_cfg);
+ if (IS_ERR(pmic->regmap)) {
+ ret = PTR_ERR(pmic->regmap);
+ dev_err(dev, "Failed to initialise regmap: %d\n", ret);
+ return ret;
+ }
+
+ ret = mfd_add_devices(dev, -1, cht_wc_dev, ARRAY_SIZE(cht_wc_dev),
+ NULL, 0, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to add devices: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cht_wc_remove(struct i2c_client *client)
+{
+ struct intel_soc_pmic *pmic = i2c_get_clientdata(client);
+
+ mfd_remove_devices(pmic->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id cht_wc_i2c_id[] = {
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cht_wc_i2c_id);
+
+static const struct acpi_device_id cht_wc_acpi_ids[] = {
+ { "INT34D3", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, cht_wc_acpi_ids);
+
+static struct i2c_driver cht_wc_driver = {
+ .driver = {
+ .name = "CHT Whiskey Cove PMIC",
+ .acpi_match_table = ACPI_PTR(cht_wc_acpi_ids),
+ },
+ .probe = cht_wc_probe,
+ .remove = cht_wc_remove,
+ .id_table = cht_wc_i2c_id,
+};
+
+module_i2c_driver(cht_wc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Hans de Goede <hdegoede@xxxxxxxxxx>");
diff --git a/include/linux/mfd/intel_chtwc.h b/include/linux/mfd/intel_chtwc.h
new file mode 100644
index 0000000..1fb356f
--- /dev/null
+++ b/include/linux/mfd/intel_chtwc.h
@@ -0,0 +1,75 @@
+/*
+ * intel_chtwc.h - Header file for Intel Cherrytrail Whiskey Cove PMIC
+ *
+ * Copyright (C) 2017 Hans de Goede <hdegoede@xxxxxxxxxx>
+ *
+ * Based on various non upstream patches to support the CHT Whiskey Cove PMIC:
+ * Copyright (C) 2013-2015 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#ifndef __INTEL_CHTWC_H__
+#define __INTEL_CHTWC_H__
+
+#define CHT_WC_DEVICE1_ADDR 0x6e
+
+#define CHT_WC_V1P05A_CTRL 0x6e3b
+#define CHT_WC_V1P15_CTRL 0x6e3c
+#define CHT_WC_V1P05A_VSEL 0x6e3d
+#define CHT_WC_V1P15_VSEL 0x6e3e
+#define CHT_WC_V1P8A_CTRL 0x6e56
+#define CHT_WC_V1P8SX_CTRL 0x6e57
+#define CHT_WC_VDDQ_CTRL 0x6e58
+#define CHT_WC_V1P2A_CTRL 0x6e59
+#define CHT_WC_V1P2SX_CTRL 0x6e5a
+#define CHT_WC_V1P8A_VSEL 0x6e5b
+#define CHT_WC_VDDQ_VSEL 0x6e5c
+#define CHT_WC_V2P8SX_CTRL 0x6e5d
+#define CHT_WC_V3P3A_CTRL 0x6e5e
+#define CHT_WC_V3P3SD_CTRL 0x6e5f
+#define CHT_WC_VSDIO_CTRL 0x6e67
+#define CHT_WC_V3P3A_VSEL 0x6e68
+#define CHT_WC_VPROG1A_CTRL 0x6e90
+#define CHT_WC_VPROG1B_CTRL 0x6e91
+#define CHT_WC_VPROG1F_CTRL 0x6e95
+#define CHT_WC_VPROG2D_CTRL 0x6e99
+#define CHT_WC_VPROG3A_CTRL 0x6e9a
+#define CHT_WC_VPROG3B_CTRL 0x6e9b
+#define CHT_WC_VPROG4A_CTRL 0x6e9c
+#define CHT_WC_VPROG4B_CTRL 0x6e9d
+#define CHT_WC_VPROG4C_CTRL 0x6e9e
+#define CHT_WC_VPROG4D_CTRL 0x6e9f
+#define CHT_WC_VPROG5A_CTRL 0x6ea0
+#define CHT_WC_VPROG5B_CTRL 0x6ea1
+#define CHT_WC_VPROG6A_CTRL 0x6ea2
+#define CHT_WC_VPROG6B_CTRL 0x6ea3
+#define CHT_WC_VPROG1A_VSEL 0x6ec0
+#define CHT_WC_VPROG1B_VSEL 0x6ec1
+#define CHT_WC_V1P8SX_VSEL 0x6ec2
+#define CHT_WC_V1P2SX_VSEL 0x6ec3
+#define CHT_WC_V1P2A_VSEL 0x6ec4
+#define CHT_WC_VPROG1F_VSEL 0x6ec5
+#define CHT_WC_VSDIO_VSEL 0x6ec6
+#define CHT_WC_V2P8SX_VSEL 0x6ec7
+#define CHT_WC_V3P3SD_VSEL 0x6ec8
+#define CHT_WC_VPROG2D_VSEL 0x6ec9
+#define CHT_WC_VPROG3A_VSEL 0x6eca
+#define CHT_WC_VPROG3B_VSEL 0x6ecb
+#define CHT_WC_VPROG4A_VSEL 0x6ecc
+#define CHT_WC_VPROG4B_VSEL 0x6ecd
+#define CHT_WC_VPROG4C_VSEL 0x6ece
+#define CHT_WC_VPROG4D_VSEL 0x6ecf
+#define CHT_WC_VPROG5A_VSEL 0x6ed0
+#define CHT_WC_VPROG5B_VSEL 0x6ed1
+#define CHT_WC_VPROG6A_VSEL 0x6ed2
+#define CHT_WC_VPROG6B_VSEL 0x6ed3
+
+#endif
--
2.9.3