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....@intel.com>
Cc: Felipe Balbi <felipe.ba...@linux.intel.com>
Cc: Andy Shevchenko <andriy.shevche...@linux.intel.com>
Signed-off-by: Hans de Goede <hdego...@redhat.com>
---
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 <hdego...@redhat.com>
+ *
+ * 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 <hdego...@redhat.com>");
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 <hdego...@redhat.com>
+ *
+ * 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

Reply via email to