From fa036da139faf90f2d4764f8781719d4cb4d1bab Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Mon, 4 Jun 2018 17:21:50 -0500 Subject: [PATCH] misc: cs40l2x: Add GPIO1 enable/disable support * Added a sysfs control for enabling and disabling GPIO1. * Added a means to disable GPIO1 by default based on a device tree entry. * Added a means to automatically control GPIO1 as a function of the host's power state based on a device tree entry. * Updated the minimum supported firmware revision to 4.2.0. Change-Id: I654da852bc9982cbc6628666b82141363651ea72 Signed-off-by: Jeff LaBundy Signed-off-by: Ravi Vembu Mot-CRs-Fixed: (CR) Reviewed-on: https://gerrit.mot.com/1202246 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Submit-Approved: Jira Key --- drivers/misc/cs40l2x.c | 115 ++++++++++++++++++++++++++ drivers/misc/cs40l2x.h | 10 ++- include/linux/platform_data/cs40l2x.h | 1 + 3 files changed, 125 insertions(+), 1 deletion(-) diff --git a/drivers/misc/cs40l2x.c b/drivers/misc/cs40l2x.c index 3409308a9dc3..96140c0c08fd 100644 --- a/drivers/misc/cs40l2x.c +++ b/drivers/misc/cs40l2x.c @@ -390,6 +390,54 @@ static unsigned int cs40l2x_dsp_reg(struct cs40l2x_private *cs40l2x, return CS40L2X_DEVID; } +static ssize_t cs40l2x_gpio1_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + mutex_lock(&cs40l2x->lock); + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GPIO_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), &val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read GPIO1 configuration\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs40l2x_gpio1_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + ret = kstrtou32(buf, 10, &val); + if (ret) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GPIO_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), + val ? CS40L2X_GPIO1_ENABLED : CS40L2X_GPIO1_DISABLED); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to write GPIO1 configuration\n"); + return ret; + } + + return count; +} + static ssize_t cs40l2x_gpio1_rise_index_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1064,6 +1112,8 @@ static DEVICE_ATTR(cp_trigger_index, 0660, cs40l2x_cp_trigger_index_show, cs40l2x_cp_trigger_index_store); static DEVICE_ATTR(cp_trigger_queue, 0660, cs40l2x_cp_trigger_queue_show, cs40l2x_cp_trigger_queue_store); +static DEVICE_ATTR(gpio1_enable, 0660, cs40l2x_gpio1_enable_show, + cs40l2x_gpio1_enable_store); static DEVICE_ATTR(gpio1_rise_index, 0660, cs40l2x_gpio1_rise_index_show, cs40l2x_gpio1_rise_index_store); static DEVICE_ATTR(gpio1_fall_index, 0660, cs40l2x_gpio1_fall_index_show, @@ -1092,6 +1142,7 @@ static DEVICE_ATTR(num_waves, 0660, cs40l2x_num_waves_show, NULL); static struct attribute *cs40l2x_dev_attrs[] = { &dev_attr_cp_trigger_index.attr, &dev_attr_cp_trigger_queue.attr, + &dev_attr_gpio1_enable.attr, &dev_attr_gpio1_rise_index.attr, &dev_attr_gpio1_fall_index.attr, &dev_attr_gpio1_fall_timeout.attr, @@ -1787,6 +1838,17 @@ static void cs40l2x_dsp_start(struct cs40l2x_private *cs40l2x) unsigned int val; int dsp_timeout = CS40L2X_DSP_TIMEOUT_COUNT; + if (cs40l2x->pdata.gpio1_mode != CS40L2X_GPIO1_MODE_DEF_ON) { + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GPIO_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), + CS40L2X_GPIO1_DISABLED); + if (ret) { + dev_err(dev, "Failed to pre-configure GPIO1\n"); + return; + } + } + switch (cs40l2x->revid) { case CS40L2X_REVID_A0: ret = regmap_update_bits(regmap, CS40L2X_PWR_CTRL1, @@ -2668,6 +2730,14 @@ static int cs40l2x_handle_of_data(struct i2c_client *i2c_client, if (!ret) pdata->gpio1_fall_timeout = out_val | CS40L2X_PDATA_PRESENT; + ret = of_property_read_u32(np, "cirrus,gpio1-mode", &out_val); + if (!ret) { + if (out_val > CS40L2X_GPIO1_MODE_MAX) + dev_warn(dev, "Ignored default gpio1_mode\n"); + else + pdata->gpio1_mode = out_val; + } + return 0; } @@ -3036,6 +3106,50 @@ static int cs40l2x_i2c_remove(struct i2c_client *i2c_client) return 0; } +static int __maybe_unused cs40l2x_suspend(struct device *dev) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret = 0; + + mutex_lock(&cs40l2x->lock); + + if (cs40l2x->pdata.gpio1_mode == CS40L2X_GPIO1_MODE_AUTO) { + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GPIO_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), + CS40L2X_GPIO1_ENABLED); + if (ret) + dev_err(dev, "Failed to enable GPIO1 upon suspend\n"); + } + + mutex_unlock(&cs40l2x->lock); + + return ret; +} + +static int __maybe_unused cs40l2x_resume(struct device *dev) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret = 0; + + mutex_lock(&cs40l2x->lock); + + if (cs40l2x->pdata.gpio1_mode == CS40L2X_GPIO1_MODE_AUTO) { + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GPIO_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), + CS40L2X_GPIO1_DISABLED); + if (ret) + dev_err(dev, "Failed to disable GPIO1 upon resume\n"); + } + + mutex_unlock(&cs40l2x->lock); + + return ret; +} + +static SIMPLE_DEV_PM_OPS(cs40l2x_pm_ops, cs40l2x_suspend, cs40l2x_resume); + static const struct of_device_id cs40l2x_of_match[] = { { .compatible = "cirrus,cs40l20" }, { .compatible = "cirrus,cs40l25" }, @@ -3060,6 +3174,7 @@ static struct i2c_driver cs40l2x_i2c_driver = { .driver = { .name = "cs40l2x", .of_match_table = cs40l2x_of_match, + .pm = &cs40l2x_pm_ops, }, .id_table = cs40l2x_id, .probe = cs40l2x_i2c_probe, diff --git a/drivers/misc/cs40l2x.h b/drivers/misc/cs40l2x.h index fd5791c376c0..c9373f839c8c 100644 --- a/drivers/misc/cs40l2x.h +++ b/drivers/misc/cs40l2x.h @@ -671,7 +671,7 @@ #define CS40L2X_ALGO_REV_MASK 0xFF0000 #define CS40L2X_NUM_ALGOS_MAX 16 -#define CS40L2X_FW_REV_MIN 0x040100 +#define CS40L2X_FW_REV_MIN 0x040200 #define CS40L2X_ALGO_ID_SIZE 4 #define CS40L2X_COEFF_COUNT_SIZE 4 @@ -746,6 +746,14 @@ #define CS40L2X_PDATA_PRESENT 0x80000000 #define CS40L2X_PDATA_MASK ~CS40L2X_PDATA_PRESENT +#define CS40L2X_GPIO1_MODE_DEF_ON 0 +#define CS40L2X_GPIO1_MODE_DEF_OFF 1 +#define CS40L2X_GPIO1_MODE_AUTO 2 +#define CS40L2X_GPIO1_MODE_MAX CS40L2X_GPIO1_MODE_AUTO + +#define CS40L2X_GPIO1_ENABLED 1 +#define CS40L2X_GPIO1_DISABLED 0 + bool cs40l2x_readable_reg(struct device *dev, unsigned int reg); bool cs40l2x_precious_reg(struct device *dev, unsigned int reg); diff --git a/include/linux/platform_data/cs40l2x.h b/include/linux/platform_data/cs40l2x.h index 7d2c02c12287..881a379f2566 100644 --- a/include/linux/platform_data/cs40l2x.h +++ b/include/linux/platform_data/cs40l2x.h @@ -26,6 +26,7 @@ struct cs40l2x_platform_data { unsigned int gpio1_rise_index; unsigned int gpio1_fall_index; unsigned int gpio1_fall_timeout; + unsigned int gpio1_mode; }; #endif /* __CS40L2X_H */