@@ -154,10 +154,16 @@ static const struct regmap_config adp5585_regmap_config_template = {
static const struct adp5585_regs adp5585_regs = {
.ext_cfg = ADP5585_PIN_CONFIG_C,
+ .int_en = ADP5585_INT_EN,
+ .gen_cfg = ADP5585_GENERAL_CFG,
+ .poll_ptime_cfg = ADP5585_POLL_PTIME_CFG,
};
static const struct adp5585_regs adp5589_regs = {
.ext_cfg = ADP5589_PIN_CONFIG_D,
+ .int_en = ADP5589_INT_EN,
+ .gen_cfg = ADP5589_GENERAL_CFG,
+ .poll_ptime_cfg = ADP5589_POLL_PTIME_CFG,
};
static int adp5585_fill_chip_configs(struct adp5585_dev *adp5585,
@@ -214,6 +220,8 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
{
unsigned int has_pwm = 0, has_gpio = 0, rc = 0;
const struct mfd_cell *cells;
+ unsigned int prop_val;
+ int ret;
if (device_property_present(dev, "#pwm-cells"))
has_pwm = 1;
@@ -224,6 +232,25 @@ static int adp5585_parse_fw(struct device *dev, struct adp5585_dev *adp5585,
if (!has_pwm && !has_gpio)
return -ENODEV;
+ ret = device_property_read_u32(dev, "poll-interval", &prop_val);
+ if (!ret) {
+ switch (prop_val) {
+ case 10:
+ fallthrough;
+ case 20:
+ fallthrough;
+ case 30:
+ fallthrough;
+ case 40:
+ adp5585->ev_poll_time = prop_val / 10 - 1;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid value(%u) for poll-interval\n",
+ prop_val);
+ }
+ }
+
*devs = devm_kcalloc(dev, has_pwm + has_gpio, sizeof(struct mfd_cell),
GFP_KERNEL);
if (!*devs)
@@ -249,6 +276,135 @@ static void adp5585_osc_disable(void *data)
regmap_write(adp5585->regmap, ADP5585_GENERAL_CFG, 0);
}
+static void adp5585_report_events(struct adp5585_dev *adp5585, int ev_cnt)
+{
+ struct adp5585_ev_handler *h;
+ unsigned int i;
+
+ guard(mutex)(&adp5585->ev_lock);
+
+ if (list_empty(&adp5585->ev_handlers)) {
+ dev_warn_ratelimited(adp5585->dev, "No event handlers registered\n");
+ return;
+ }
+
+ for (i = 0; i < ev_cnt; i++) {
+ unsigned int key, key_val, key_press;
+ int ret;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, &key);
+ if (ret)
+ return;
+
+ key_val = FIELD_GET(ADP5585_KEY_EVENT_MASK, key);
+ key_press = FIELD_GET(ADP5585_KEV_EV_PRESS_MASK, key);
+
+ list_for_each_entry(h, &adp5585->ev_handlers, entry) {
+ ret = h->handler(h->dev, key_val, key_press);
+ if (!ret)
+ /* handled! */
+ break;
+ }
+ }
+}
+
+static irqreturn_t adp5585_irq(int irq, void *data)
+{
+ struct adp5585_dev *adp5585 = data;
+ unsigned int status, ev_cnt;
+ int ret;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &status);
+ if (ret)
+ return IRQ_HANDLED;
+
+ if (status & ADP5585_OVRFLOW_INT)
+ dev_err_ratelimited(adp5585->dev, "Event Overflow Error\n");
+
+ if (!(status & ADP5585_EVENT_INT))
+ goto out_irq;
+
+ ret = regmap_read(adp5585->regmap, ADP5585_STATUS, &ev_cnt);
+ if (ret)
+ goto out_irq;
+
+ ev_cnt = FIELD_GET(ADP5585_EC_MASK, ev_cnt);
+ if (!ev_cnt)
+ goto out_irq;
+
+ adp5585_report_events(adp5585, ev_cnt);
+out_irq:
+ regmap_write(adp5585->regmap, ADP5585_INT_STATUS, status);
+ return IRQ_HANDLED;
+}
+
+static int adp5585_setup(struct adp5585_dev *adp5585)
+{
+ const struct adp5585_regs *regs = adp5585->info->regs;
+ unsigned int reg_val, i;
+ int ret;
+
+ for (i = 0; i < ADP5585_EV_MAX; i++) {
+ ret = regmap_read(adp5585->regmap, ADP5585_FIFO_1 + i, ®_val);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_write(adp5585->regmap, regs->poll_ptime_cfg,
+ adp5585->ev_poll_time);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adp5585->regmap, regs->gen_cfg,
+ ADP5585_OSC_FREQ_500KHZ | ADP5585_INT_CFG |
+ ADP5585_OSC_EN);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(adp5585->dev, adp5585_osc_disable,
+ adp5585);
+}
+
+static void adp5585_irq_disable(void *data)
+{
+ struct adp5585_dev *adp5585 = data;
+
+ regmap_write(adp5585->regmap, adp5585->info->regs->int_en, 0);
+}
+
+static int adp5585_irq_enable(struct i2c_client *i2c,
+ struct adp5585_dev *adp5585)
+{
+ const struct adp5585_regs *regs = adp5585->info->regs;
+ unsigned int stat;
+ int ret;
+
+ if (i2c->irq <= 0)
+ return 0;
+
+ ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, adp5585_irq,
+ IRQF_ONESHOT, i2c->name, adp5585);
+ if (ret)
+ return ret;
+
+ /* clear any possible outstanding interrupt before enabling them... */
+ ret = regmap_read(adp5585->regmap, ADP5585_INT_STATUS, &stat);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adp5585->regmap, ADP5585_INT_STATUS, stat);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(adp5585->regmap, regs->int_en,
+ ADP5585_OVRFLOW_IEN | ADP5585_EVENT_IEN);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(&i2c->dev, adp5585_irq_disable,
+ adp5585);
+}
+
static int adp5585_i2c_probe(struct i2c_client *i2c)
{
struct regmap_config regmap_config;
@@ -282,16 +438,19 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
return dev_err_probe(&i2c->dev, -ENODEV,
"Invalid device ID 0x%02x\n", id);
+ adp5585->dev = &i2c->dev;
+ adp5585->irq = i2c->irq;
+ INIT_LIST_HEAD(&adp5585->ev_handlers);
+
n_devs = adp5585_parse_fw(&i2c->dev, adp5585, &devs);
if (n_devs < 0)
return n_devs;
- ret = regmap_set_bits(adp5585->regmap, ADP5585_GENERAL_CFG,
- ADP5585_OSC_EN);
+ ret = adp5585_setup(adp5585);
if (ret)
return ret;
- ret = devm_add_action_or_reset(&i2c->dev, adp5585_osc_disable, adp5585);
+ ret = devm_mutex_init(&i2c->dev, &adp5585->ev_lock);
if (ret)
return ret;
@@ -301,13 +460,16 @@ static int adp5585_i2c_probe(struct i2c_client *i2c)
return dev_err_probe(&i2c->dev, ret,
"Failed to add child devices\n");
- return 0;
+ return adp5585_irq_enable(i2c, adp5585);
}
static int adp5585_suspend(struct device *dev)
{
struct adp5585_dev *adp5585 = dev_get_drvdata(dev);
+ if (adp5585->irq)
+ disable_irq(adp5585->irq);
+
regcache_cache_only(adp5585->regmap, true);
return 0;
@@ -316,11 +478,19 @@ static int adp5585_suspend(struct device *dev)
static int adp5585_resume(struct device *dev)
{
struct adp5585_dev *adp5585 = dev_get_drvdata(dev);
+ int ret;
regcache_cache_only(adp5585->regmap, false);
regcache_mark_dirty(adp5585->regmap);
- return regcache_sync(adp5585->regmap);
+ ret = regcache_sync(adp5585->regmap);
+ if (ret)
+ return ret;
+
+ if (adp5585->irq)
+ enable_irq(adp5585->irq);
+
+ return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(adp5585_pm, adp5585_suspend, adp5585_resume);
@@ -10,13 +10,23 @@
#define __MFD_ADP5585_H_
#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
#define ADP5585_ID 0x00
#define ADP5585_MAN_ID_VALUE 0x20
#define ADP5585_MAN_ID_MASK GENMASK(7, 4)
+#define ADP5585_REV_ID_MASK GENMASK(3, 0)
#define ADP5585_INT_STATUS 0x01
+#define ADP5585_OVRFLOW_INT BIT(2)
+#define ADP5585_EVENT_INT BIT(0)
#define ADP5585_STATUS 0x02
+#define ADP5585_EC_MASK GENMASK(4, 0)
#define ADP5585_FIFO_1 0x03
+#define ADP5585_KEV_EV_PRESS_MASK BIT(7)
+#define ADP5585_KEY_EVENT_MASK GENMASK(6, 0)
#define ADP5585_FIFO_2 0x04
#define ADP5585_FIFO_3 0x05
#define ADP5585_FIFO_4 0x06
@@ -32,6 +42,7 @@
#define ADP5585_FIFO_14 0x10
#define ADP5585_FIFO_15 0x11
#define ADP5585_FIFO_16 0x12
+#define ADP5585_EV_MAX (ADP5585_FIFO_16 - ADP5585_FIFO_1 + 1)
#define ADP5585_GPI_INT_STAT_A 0x13
#define ADP5585_GPI_INT_STAT_B 0x14
#define ADP5585_GPI_STATUS_A 0x15
@@ -104,6 +115,8 @@
#define ADP5585_INT_CFG BIT(1)
#define ADP5585_RST_CFG BIT(0)
#define ADP5585_INT_EN 0x3c
+#define ADP5585_OVRFLOW_IEN BIT(2)
+#define ADP5585_EVENT_IEN BIT(0)
#define ADP5585_MAX_REG ADP5585_INT_EN
@@ -121,7 +134,9 @@
#define ADP5589_PWM_OFFT_LOW 0x3e
#define ADP5589_PWM_ONT_LOW 0x40
#define ADP5589_PWM_CFG 0x42
+#define ADP5589_POLL_PTIME_CFG 0x48
#define ADP5589_PIN_CONFIG_D 0x4C
+#define ADP5589_GENERAL_CFG 0x4d
#define ADP5589_INT_EN 0x4e
#define ADP5589_MAX_REG ADP5589_INT_EN
@@ -138,8 +153,18 @@ enum adp5585_regmap_type {
ADP5589_REGMAP_02,
};
+struct adp5585_ev_handler {
+ struct list_head entry;
+ struct device *dev;
+ int (*handler)(struct device *dev, unsigned int key_val,
+ unsigned int key_press);
+};
+
struct adp5585_regs {
+ unsigned int gen_cfg;
unsigned int ext_cfg;
+ unsigned int int_en;
+ unsigned int poll_ptime_cfg;
};
struct adp5585_info {
@@ -150,7 +175,30 @@ struct adp5585_info {
struct adp5585_dev {
struct regmap *regmap;
+ struct device *dev;
const struct adp5585_info *info;
+ /* Used to synchronize the availability of the event handlers */
+ struct mutex ev_lock;
+ struct list_head ev_handlers;
+ int irq;
+ unsigned int ev_poll_time;
};
+static inline void adp5585_ev_handler_remove(void *data)
+{
+ struct adp5585_ev_handler *handler = data;
+ struct adp5585_dev *adp5585 = dev_get_drvdata(handler->dev->parent);
+
+ guard(mutex)(&adp5585->ev_lock);
+ list_del(&handler->entry);
+}
+
+static inline int devm_adp5585_ev_handler_add(struct adp5585_dev *adp5585,
+ struct adp5585_ev_handler *handler)
+{
+ guard(mutex)(&adp5585->ev_lock);
+ list_add_tail(&handler->entry, &adp5585->ev_handlers);
+ return devm_add_action_or_reset(handler->dev, adp5585_ev_handler_remove,
+ handler);
+}
#endif