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 <jeff.labundy@cirrus.com>
Signed-off-by: Ravi Vembu <raviv@motorola.com>
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
This commit is contained in:
Jeff LaBundy 2018-06-04 17:21:50 -05:00 • committed by Ravikumar Vembu
commit fa036da139
3 changed files with 125 additions and 1 deletions

View file

@ -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,

View file

@ -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);

View file

@ -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 */