mfd: pm8058: Modify pm8058 subdevices to pm8xxx interface
Move the following subdevices to use the pm8xxx interface -
mpp, irq, gpio, keypad, power-key, leds, othc, vibrator,
rtc, batt-alarm, thermal, upl, nfc, pwm, xoadc, regulators,
xo-buffers, charger.
This allows usage of a common driver for modules which are same
across multiple PM8XXX PMICs. It also provides flexibility
to add/remove subdevices for multiple board configurations.
Change-Id: Id9795552fc9f4a2c920c070babfaef1f4cd6ca61
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
diff --git a/drivers/misc/pm8xxx-upl.c b/drivers/misc/pm8xxx-upl.c
new file mode 100644
index 0000000..d3d9746f
--- /dev/null
+++ b/drivers/misc/pm8xxx-upl.c
@@ -0,0 +1,350 @@
+/* Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+/*
+ * Qualcomm PM8XXX UPL driver
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/upl.h>
+
+/* PMIC8XXX UPL registers */
+#define SSBI_REG_UPL_CTRL 0x17B
+#define SSBI_REG_UPL_TRUTHTABLE1 0x17C
+#define SSBI_REG_UPL_TRUTHTABLE2 0x17D
+
+struct pm8xxx_upl_device {
+ struct device *dev;
+ struct mutex upl_mutex;
+#if defined(CONFIG_DEBUG_FS)
+ struct dentry *dent;
+#endif
+};
+static struct pm8xxx_upl_device *upl_dev;
+
+/* APIs */
+
+/*
+ * pm8xxx_upl_request - request a handle to access UPL device
+ */
+struct pm8xxx_upl_device *pm8xxx_upl_request(void)
+{
+ return upl_dev;
+}
+EXPORT_SYMBOL(pm8xxx_upl_request);
+
+/*
+ * pm8xxx_upl_read_truthtable - read value currently stored in UPL truth table
+ *
+ * @upldev: the UPL device
+ * @truthtable: value read from UPL truth table
+ */
+int pm8xxx_upl_read_truthtable(struct pm8xxx_upl_device *upldev,
+ u16 *truthtable)
+{
+ int rc = 0;
+ u8 table[2];
+
+ if (upldev == NULL || IS_ERR(upldev))
+ return -EINVAL;
+
+ mutex_lock(&upldev->upl_mutex);
+
+ rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE1,
+ &(table[0]));
+ if (rc) {
+ pr_err("%s: FAIL pm8xxx_readb(0x%X)=0x%02X: rc=%d\n",
+ __func__, SSBI_REG_UPL_TRUTHTABLE1, table[0], rc);
+ goto upl_read_done;
+ }
+
+ rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE2,
+ &(table[1]));
+ if (rc)
+ pr_err("%s: FAIL pm8xxx_readb(0x%X)=0x%02X: rc=%d\n",
+ __func__, SSBI_REG_UPL_TRUTHTABLE2, table[1], rc);
+upl_read_done:
+ mutex_unlock(&upldev->upl_mutex);
+ *truthtable = (((u16)table[1]) << 8) | table[0];
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_upl_read_truthtable);
+
+/*
+ * pm8xxx_upl_writes_truthtable - write value into UPL truth table
+ *
+ * @upldev: the UPL device
+ * @truthtable: value written to UPL truth table
+ *
+ * Each bit in parameter "truthtable" corresponds to the UPL output for a given
+ * set of input pin values. For example, if the input pins have the following
+ * values: A=1, B=1, C=1, D=0, then the UPL would output the value of bit 14
+ * (0b1110) in parameter "truthtable".
+ */
+int pm8xxx_upl_write_truthtable(struct pm8xxx_upl_device *upldev,
+ u16 truthtable)
+{
+ int rc = 0;
+ u8 table[2];
+
+ if (upldev == NULL || IS_ERR(upldev))
+ return -EINVAL;
+
+ table[0] = truthtable & 0xFF;
+ table[1] = (truthtable >> 8) & 0xFF;
+
+ mutex_lock(&upldev->upl_mutex);
+
+ rc = pm8xxx_writeb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE1,
+ table[0]);
+ if (rc) {
+ pr_err("%s: FAIL pm8xxx_writeb(0x%X)=0x%04X: rc=%d\n",
+ __func__, SSBI_REG_UPL_TRUTHTABLE1, table[0], rc);
+ goto upl_write_done;
+ }
+
+ rc = pm8xxx_writeb(upldev->dev->parent, SSBI_REG_UPL_TRUTHTABLE2,
+ table[1]);
+ if (rc)
+ pr_err("%s: FAIL pm8xxx_writeb(0x%X)=0x%04X: rc=%d\n",
+ __func__, SSBI_REG_UPL_TRUTHTABLE2, table[1], rc);
+upl_write_done:
+ mutex_unlock(&upldev->upl_mutex);
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_upl_write_truthtable);
+
+/*
+ * pm8xxx_upl_config - configure UPL I/O settings and UPL enable/disable
+ *
+ * @upldev: the UPL device
+ * @mask: setting mask to configure
+ * @flags: setting flags
+ */
+int pm8xxx_upl_config(struct pm8xxx_upl_device *upldev, u32 mask, u32 flags)
+{
+ int rc;
+ u8 upl_ctrl, m, f;
+
+ if (upldev == NULL || IS_ERR(upldev))
+ return -EINVAL;
+
+ mutex_lock(&upldev->upl_mutex);
+
+ rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_CTRL, &upl_ctrl);
+ if (rc) {
+ pr_err("%s: FAIL pm8xxx_readb(0x%X)=0x%02X: rc=%d\n",
+ __func__, SSBI_REG_UPL_CTRL, upl_ctrl, rc);
+ goto upl_config_done;
+ }
+
+ m = mask & 0x00ff;
+ f = flags & 0x00ff;
+ upl_ctrl &= ~m;
+ upl_ctrl |= m & f;
+
+ rc = pm8xxx_writeb(upldev->dev->parent, SSBI_REG_UPL_CTRL, upl_ctrl);
+ if (rc)
+ pr_err("%s: FAIL pm8xxx_writeb(0x%X)=0x%02X: rc=%d\n",
+ __func__, SSBI_REG_UPL_CTRL, upl_ctrl, rc);
+upl_config_done:
+ mutex_unlock(&upldev->upl_mutex);
+ return rc;
+}
+EXPORT_SYMBOL(pm8xxx_upl_config);
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int truthtable_set(void *data, u64 val)
+{
+ int rc;
+
+ rc = pm8xxx_upl_write_truthtable(data, val);
+ if (rc)
+ pr_err("%s: pm8xxx_upl_write_truthtable: rc=%d, "
+ "truthtable=0x%llX\n", __func__, rc, val);
+ return rc;
+}
+
+static int truthtable_get(void *data, u64 *val)
+{
+ int rc;
+ u16 truthtable;
+
+ rc = pm8xxx_upl_read_truthtable(data, &truthtable);
+ if (rc)
+ pr_err("%s: pm8xxx_upl_read_truthtable: rc=%d, "
+ "truthtable=0x%X\n", __func__, rc, truthtable);
+ if (val)
+ *val = truthtable;
+
+ return rc;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(upl_truthtable_fops, truthtable_get,
+ truthtable_set, "0x%04llX\n");
+
+/* enter values as 0xMMMMFFFF where MMMM is the mask and FFFF is the flags */
+static int control_set(void *data, u64 val)
+{
+ u8 mask, flags;
+ int rc;
+
+ flags = val & 0xFFFF;
+ mask = (val >> 16) & 0xFFFF;
+
+ rc = pm8xxx_upl_config(data, mask, flags);
+ if (rc)
+ pr_err("%s: pm8xxx_upl_config: rc=%d, mask = 0x%X, "
+ "flags = 0x%X\n", __func__, rc, mask, flags);
+ return rc;
+}
+
+static int control_get(void *data, u64 *val)
+{
+ struct pm8xxx_upl_device *upldev;
+ int rc = 0;
+ u8 ctrl;
+
+ upldev = data;
+
+ mutex_lock(&upldev->upl_mutex);
+
+ rc = pm8xxx_readb(upldev->dev->parent, SSBI_REG_UPL_CTRL, &ctrl);
+ if (rc)
+ pr_err("%s: FAIL pm8xxx_readb(): rc=%d (ctrl=0x%02X)\n",
+ __func__, rc, ctrl);
+
+ mutex_unlock(&upldev->upl_mutex);
+
+ *val = ctrl;
+
+ return rc;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(upl_control_fops, control_get,
+ control_set, "0x%02llX\n");
+
+static int pm8xxx_upl_debug_init(struct pm8xxx_upl_device *upldev)
+{
+ struct dentry *dent;
+ struct dentry *temp;
+
+ dent = debugfs_create_dir("pm8xxx-upl", NULL);
+ if (dent == NULL || IS_ERR(dent)) {
+ pr_err("%s: ERR debugfs_create_dir: dent=0x%X\n",
+ __func__, (unsigned)dent);
+ return -ENOMEM;
+ }
+
+ temp = debugfs_create_file("truthtable", S_IRUSR | S_IWUSR, dent,
+ upldev, &upl_truthtable_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+ __func__, (unsigned)dent);
+ goto debug_error;
+ }
+
+ temp = debugfs_create_file("control", S_IRUSR | S_IWUSR, dent,
+ upldev, &upl_control_fops);
+ if (temp == NULL || IS_ERR(temp)) {
+ pr_err("%s: ERR debugfs_create_file: dent=0x%X\n",
+ __func__, (unsigned)dent);
+ goto debug_error;
+ }
+
+ upldev->dent = dent;
+ return 0;
+
+debug_error:
+ debugfs_remove_recursive(dent);
+ return -ENOMEM;
+}
+
+static int __devexit pm8xxx_upl_debug_remove(struct pm8xxx_upl_device *upldev)
+{
+ debugfs_remove_recursive(upldev->dent);
+ return 0;
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
+static int __devinit pm8xxx_upl_probe(struct platform_device *pdev)
+{
+ struct pm8xxx_upl_device *upldev;
+
+ upldev = kzalloc(sizeof *upldev, GFP_KERNEL);
+ if (upldev == NULL) {
+ pr_err("%s: kzalloc() failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&upldev->upl_mutex);
+
+ upl_dev = upldev;
+ upldev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, upldev);
+
+#if defined(CONFIG_DEBUG_FS)
+ pm8xxx_upl_debug_init(upl_dev);
+#endif
+ pr_notice("%s: OK\n", __func__);
+ return 0;
+}
+
+static int __devexit pm8xxx_upl_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_upl_device *upldev = platform_get_drvdata(pdev);
+
+#if defined(CONFIG_DEBUG_FS)
+ pm8xxx_upl_debug_remove(upldev);
+#endif
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(upldev);
+ pr_notice("%s: OK\n", __func__);
+
+ return 0;
+}
+
+static struct platform_driver pm8xxx_upl_driver = {
+ .probe = pm8xxx_upl_probe,
+ .remove = __devexit_p(pm8xxx_upl_remove),
+ .driver = {
+ .name = PM8XXX_UPL_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8xxx_upl_init(void)
+{
+ return platform_driver_register(&pm8xxx_upl_driver);
+}
+
+static void __exit pm8xxx_upl_exit(void)
+{
+ platform_driver_unregister(&pm8xxx_upl_driver);
+}
+
+module_init(pm8xxx_upl_init);
+module_exit(pm8xxx_upl_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PM8XXX UPL driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_UPL_DEV_NAME);