lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 20 Nov 2020 12:14:59 +0000
From:   Adam Ward <adam.ward@...semi.com>
To:     Mark Brown <broonie@...nel.org>, Rob Herring <robh+dt@...nel.org>
CC:     Liam Girdwood <lgirdwood@...il.com>,
        Vincent Whitchurch <vincent.whitchurch@...s.com>,
        <linux-kernel@...r.kernel.org>, <devicetree@...r.kernel.org>
Subject: [PATCH 9/9] regulator: da9121: add interrupt support

Adds interrupt handler for variants, and notifications for events; over
temperature/voltage/current.
Also handling of persistent events and respective timing configuration.

Signed-off-by: Adam Ward <Adam.Ward.opensource@...semi.com>
---
 drivers/regulator/da9121-regulator.c | 548 +++++++++++++++++++++++++++++++++++
 1 file changed, 548 insertions(+)

diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c
index 3addbd2..37a767e 100644
--- a/drivers/regulator/da9121-regulator.c
+++ b/drivers/regulator/da9121-regulator.c
@@ -24,15 +24,22 @@
 #include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/regulator/da9121.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
 
 #include "da9121-regulator.h"
 
 /* Chip data */
 struct da9121 {
 	struct device *dev;
+	struct delayed_work work;
+	struct regmap *regmap;
 	struct da9121_pdata *pdata;
 	struct regmap *regmap;
 	struct regulator_dev *rdev[DA9121_IDX_MAX];
+	unsigned int persistent[2];
+	unsigned int passive_delay;
+	int chip_irq;
 	int variant_id;
 };
 
@@ -92,6 +99,104 @@ struct da9121_variant_info {
 	{ 1, 2, &da9121_6A_2phase_current  },	//DA9121_TYPE_DA9217
 };
 
+static void da9121_status_poll_on(struct work_struct *work)
+{
+	struct da9121 *chip = container_of(work, struct da9121, work.work);
+	enum { R0 = 0, R1, R2, REG_MAX_NUM };
+	int status[REG_MAX_NUM] = {0};
+	int clear[REG_MAX_NUM] = {0};
+	unsigned long delay;
+	int i;
+	int ret;
+
+	/* If persistent-notification, status will be true
+	 * If not persistent-notification any longer, status will be false
+	 */
+	ret = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_STATUS_0,
+			(void *)status, (size_t)REG_MAX_NUM);
+	if (ret < 0) {
+		dev_err(chip->dev,
+			"Failed to read STATUS registers: %d\n", ret);
+		goto error;
+	}
+
+	/* 0 TEMP_CRIT */
+	if ((chip->persistent[R0] & DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT) &&
+	    !(status[R0] & DA9121_MASK_SYS_STATUS_0_TEMP_CRIT)) {
+		clear[R0] |= DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT;
+		chip->persistent[R0] &= ~DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT;
+	}
+	/* 0 TEMP_WARN */
+	if ((chip->persistent[R0] & DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN) &&
+	    !(status[R0] & DA9121_MASK_SYS_STATUS_0_TEMP_WARN)) {
+		clear[R0] |= DA9121_MASK_SYS_MASK_0_M_TEMP_WARN;
+		chip->persistent[R0] &= ~DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN;
+	}
+
+	/* 1 OV1 */
+	if ((chip->persistent[R1] & DA9121_MASK_SYS_EVENT_1_E_OV1) &&
+	    !(status[R1] & DA9121_MASK_SYS_STATUS_1_OV1)) {
+		clear[R1] |= DA9121_MASK_SYS_MASK_1_M_OV1;
+		chip->persistent[R1] &= ~DA9121_MASK_SYS_EVENT_1_E_OV1;
+	}
+	/* 1 UV1 */
+	if ((chip->persistent[R1] & DA9121_MASK_SYS_EVENT_1_E_UV1) &&
+	    !(status[R1] & DA9121_MASK_SYS_STATUS_1_UV1)) {
+		clear[R1] |= DA9121_MASK_SYS_MASK_1_M_UV1;
+		chip->persistent[R1] &= ~DA9121_MASK_SYS_EVENT_1_E_UV1;
+	}
+	/* 1 OC1 */
+	if ((chip->persistent[R1] & DA9121_MASK_SYS_EVENT_1_E_OC1) &&
+	    !(status[R1] & DA9121_MASK_SYS_STATUS_1_OC1)) {
+		clear[R1] |= DA9121_MASK_SYS_MASK_1_M_OC1;
+		chip->persistent[R1] &= ~DA9121_MASK_SYS_EVENT_1_E_OC1;
+	}
+
+	if (variant_parameters[chip->variant_id].num_bucks == 2) {
+		/* 1 OV2 */
+		if ((chip->persistent[R1] & DA9xxx_MASK_SYS_EVENT_1_E_OV2) &&
+		    !(status[R1] & DA9xxx_MASK_SYS_STATUS_1_OV2)) {
+			clear[R1] |= DA9xxx_MASK_SYS_MASK_1_M_OV2;
+			chip->persistent[R1] &= ~DA9xxx_MASK_SYS_EVENT_1_E_OV2;
+		}
+		/* 1 UV2 */
+		if ((chip->persistent[R1] & DA9xxx_MASK_SYS_EVENT_1_E_UV2) &&
+		    !(status[R1] & DA9xxx_MASK_SYS_STATUS_1_UV2)) {
+			clear[R1] |= DA9xxx_MASK_SYS_MASK_1_M_UV2;
+			chip->persistent[R1] &= ~DA9xxx_MASK_SYS_EVENT_1_E_UV2;
+		}
+		/* 1 OC2 */
+		if ((chip->persistent[R1] & DA9xxx_MASK_SYS_EVENT_1_E_OC2) &&
+		    !(status[R1] & DA9xxx_MASK_SYS_STATUS_1_OC2)) {
+			clear[R1] |= DA9xxx_MASK_SYS_MASK_1_M_OC2;
+			chip->persistent[R1] &= ~DA9xxx_MASK_SYS_EVENT_1_E_OC2;
+		}
+	}
+
+	for (i = R0; i < REG_MAX_NUM-1; i++) {
+		if (clear[i]) {
+			unsigned int reg = DA9121_REG_SYS_MASK_0 + i;
+			unsigned int mbit = clear[i];
+
+			ret = regmap_update_bits(chip->regmap, reg, mbit, 0);
+			if (ret < 0) {
+				dev_err(chip->dev,
+					"Failed to unmask 0x%02x %d\n",
+					reg, ret);
+				goto error;
+			}
+		}
+	}
+
+	if (chip->persistent[R0] | chip->persistent[R1]) {
+		delay = msecs_to_jiffies(chip->passive_delay);
+		queue_delayed_work(system_freezable_wq, &chip->work, delay);
+	}
+
+error:
+	return;
+}
+
 static bool da9121_rdev_to_buck_reg_mask(struct regulator_dev *rdev, bool mode,
 					 unsigned int *reg, unsigned int *msk)
 {
@@ -528,6 +633,311 @@ static struct da9121_pdata *da9121_parse_regulators_dt(struct da9121 *chip)
 }
 #endif
 
+static inline int da9121_handle_notifier(
+		struct da9121 *chip, struct regulator_dev *rdev,
+		unsigned int event_bank, unsigned int event, unsigned int ebit)
+{
+	enum { R0 = 0, R1, R2, REG_MAX_NUM };
+	unsigned long notification = 0;
+	int ret = 0;
+
+	if (event & ebit) {
+		switch (event_bank) {
+		case DA9121_REG_SYS_EVENT_0:
+			switch (event & ebit) {
+			case DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT:
+				chip->persistent[R0] |= DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT;
+				notification |= REGULATOR_EVENT_OVER_TEMP |
+						REGULATOR_EVENT_DISABLE;
+				break;
+			case DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN:
+				chip->persistent[R0] |= DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN;
+				notification |= REGULATOR_EVENT_OVER_TEMP;
+				break;
+			default:
+				dev_warn(chip->dev,
+					 "Unhandled event in bank0 0x%02x\n",
+					 event & ebit);
+				ret = -EINVAL;
+				break;
+			}
+			break;
+		case DA9121_REG_SYS_EVENT_1:
+			switch (event & ebit) {
+			case DA9121_MASK_SYS_EVENT_1_E_OV1:
+				chip->persistent[R1] |= DA9121_MASK_SYS_EVENT_1_E_OV1;
+				notification |= REGULATOR_EVENT_REGULATION_OUT;
+				break;
+			case DA9121_MASK_SYS_EVENT_1_E_UV1:
+				chip->persistent[R1] |= DA9121_MASK_SYS_EVENT_1_E_UV1;
+				notification |= REGULATOR_EVENT_UNDER_VOLTAGE;
+				break;
+			case DA9121_MASK_SYS_EVENT_1_E_OC1:
+				chip->persistent[R1] |= DA9121_MASK_SYS_EVENT_1_E_OC1;
+				notification |= REGULATOR_EVENT_OVER_CURRENT;
+				break;
+			case DA9xxx_MASK_SYS_EVENT_1_E_OV2:
+				chip->persistent[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_OV2;
+				notification |= REGULATOR_EVENT_REGULATION_OUT;
+				break;
+			case DA9xxx_MASK_SYS_EVENT_1_E_UV2:
+				chip->persistent[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_UV2;
+				notification |= REGULATOR_EVENT_UNDER_VOLTAGE;
+				break;
+			case DA9xxx_MASK_SYS_EVENT_1_E_OC2:
+				chip->persistent[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_OC2;
+				notification |= REGULATOR_EVENT_OVER_CURRENT;
+				break;
+			default:
+				dev_warn(chip->dev,
+					 "Unhandled event in bank1 0x%02x\n",
+					 event & ebit);
+				ret = -EINVAL;
+				break;
+			}
+			break;
+		default:
+			dev_warn(chip->dev,
+				 "Unhandled event bank 0x%02x\n", event_bank);
+			ret = -EINVAL;
+			goto error;
+		}
+
+		regulator_notifier_call_chain(rdev, notification, NULL);
+	}
+
+error:
+	return ret;
+}
+
+static irqreturn_t da9121_irq_handler(int irq, void *data)
+{
+	struct da9121 *chip = data;
+	struct regulator_dev *rdev;
+	enum { R0 = 0, R1, R2, REG_MAX_NUM };
+	int event[REG_MAX_NUM] = {0};
+	int handled[REG_MAX_NUM] = {0};
+	int mask[REG_MAX_NUM] = {0};
+	int ret = IRQ_NONE;
+	int i;
+	int err;
+
+	err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_EVENT_0,
+			(void *)event, (size_t)REG_MAX_NUM);
+	if (err < 0) {
+		dev_err(chip->dev, "Failed to read EVENT registers %d\n", err);
+		ret = IRQ_NONE;
+		goto error;
+	}
+
+	err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_MASK_0,
+			(void *)mask, (size_t)REG_MAX_NUM);
+	if (err < 0) {
+		dev_err(chip->dev,
+			"Failed to read MASK registers: %d\n", ret);
+		ret = IRQ_NONE;
+		goto error;
+	}
+
+	rdev = chip->rdev[DA9121_IDX_BUCK1];
+
+	/* 0 SYSTEM_GOOD */
+	if (!(mask[R0] & DA9xxx_MASK_SYS_MASK_0_M_SG) &&
+	    (event[R0] & DA9xxx_MASK_SYS_EVENT_0_E_SG)) {
+		dev_warn(chip->dev, "Handled E_SG\n");
+		handled[R0] |= DA9xxx_MASK_SYS_EVENT_0_E_SG;
+		ret = IRQ_HANDLED;
+	}
+
+	/* 0 TEMP_CRIT */
+	if (!(mask[R0] & DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT) &&
+	    (event[R0] & DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT)) {
+		err = da9121_handle_notifier(chip, rdev,
+			DA9121_REG_SYS_EVENT_0,	event[R0],
+			DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT);
+		if (!err) {
+			handled[R0] |= DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT;
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	/* 0 TEMP_WARN */
+	if (!(mask[R0] & DA9121_MASK_SYS_MASK_0_M_TEMP_WARN) &&
+	    (event[R0] & DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN)) {
+		err = da9121_handle_notifier(chip, rdev,
+			DA9121_REG_SYS_EVENT_0, event[R0],
+			DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN);
+		if (!err) {
+			handled[R0] |= DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN;
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	if (event[R0] != handled[R0]) {
+		dev_warn(chip->dev,
+			 "Unhandled event in bank0 0x%02x\n",
+			 event[R0] ^ handled[R0]);
+	}
+
+	/* 1 PG1 */
+	if (!(mask[R1] & DA9121_MASK_SYS_MASK_1_M_PG1) &&
+	    (event[R1] & DA9121_MASK_SYS_EVENT_1_E_PG1)) {
+		dev_warn(chip->dev, "Handled E_PG1\n");
+		handled[R1] |= DA9121_MASK_SYS_EVENT_1_E_PG1;
+		ret = IRQ_HANDLED;
+	}
+
+	/* 1 OV1 */
+	if (!(mask[R1] & DA9121_MASK_SYS_MASK_1_M_OV1) &&
+	    (event[R1] & DA9121_MASK_SYS_EVENT_1_E_OV1)) {
+		err = da9121_handle_notifier(chip, rdev,
+			DA9121_REG_SYS_EVENT_1,	event[R1],
+			DA9121_MASK_SYS_EVENT_1_E_OV1);
+		if (!err) {
+			handled[R1] |= DA9121_MASK_SYS_EVENT_1_E_OV1;
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	/* 1 UV1 */
+	if (!(mask[R1] & DA9121_MASK_SYS_MASK_1_M_UV1) &&
+	    (event[R1] & DA9121_MASK_SYS_EVENT_1_E_UV1)) {
+		err = da9121_handle_notifier(chip, rdev,
+			DA9121_REG_SYS_EVENT_1,	event[R1],
+			DA9121_MASK_SYS_EVENT_1_E_UV1);
+		if (!err) {
+			handled[R1] |= DA9121_MASK_SYS_EVENT_1_E_UV1;
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	/* 1 OC1 */
+	if (!(mask[R1] & DA9121_MASK_SYS_MASK_1_M_OC1) &&
+	    (event[R1] & DA9121_MASK_SYS_EVENT_1_E_OC1)) {
+		err = da9121_handle_notifier(chip, rdev,
+			DA9121_REG_SYS_EVENT_1,	event[R1],
+			DA9121_MASK_SYS_EVENT_1_E_OC1);
+		if (!err) {
+			handled[R1] |= DA9121_MASK_SYS_EVENT_1_E_OC1;
+			ret = IRQ_HANDLED;
+		}
+	}
+
+	if (variant_parameters[chip->variant_id].num_bucks == 2) {
+		struct regulator_dev *rdev2 = chip->rdev[DA9121_IDX_BUCK2];
+
+		/* 1 PG2 */
+		if (!(mask[R1] & DA9xxx_MASK_SYS_MASK_1_M_PG2) &&
+		    (event[R1] & DA9xxx_MASK_SYS_EVENT_1_E_PG2)) {
+			dev_warn(chip->dev, "Handled E_PG2\n");
+			handled[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_PG2;
+			ret = IRQ_HANDLED;
+		}
+
+		/* 1 OV2 */
+		if (!(mask[R1] & DA9xxx_MASK_SYS_MASK_1_M_OV2) &&
+		    (event[R1] & DA9xxx_MASK_SYS_EVENT_1_E_OV2)) {
+			err = da9121_handle_notifier(chip, rdev2,
+				DA9121_REG_SYS_EVENT_1,	event[R1],
+				DA9xxx_MASK_SYS_EVENT_1_E_OV2);
+			if (!err) {
+				handled[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_OV2;
+				ret = IRQ_HANDLED;
+			}
+		}
+
+		/* 1 UV2 */
+		if (!(mask[R1] & DA9xxx_MASK_SYS_MASK_1_M_UV2) &&
+		    (event[R1] & DA9xxx_MASK_SYS_EVENT_1_E_UV2)) {
+			err = da9121_handle_notifier(chip, rdev2,
+				DA9121_REG_SYS_EVENT_1,	event[R1],
+				DA9xxx_MASK_SYS_EVENT_1_E_UV2);
+			if (!err) {
+				handled[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_UV2;
+				ret = IRQ_HANDLED;
+			}
+		}
+
+		/* 1 OC2 */
+		if (!(mask[R1] & DA9xxx_MASK_SYS_MASK_1_M_OC2) &&
+		    (event[R1] & DA9xxx_MASK_SYS_EVENT_1_E_OC2)) {
+			err = da9121_handle_notifier(chip, rdev2,
+				DA9121_REG_SYS_EVENT_1,	event[R1],
+				DA9xxx_MASK_SYS_EVENT_1_E_OC2);
+			if (!err) {
+				handled[R1] |= DA9xxx_MASK_SYS_EVENT_1_E_OC2;
+				ret = IRQ_HANDLED;
+			}
+		}
+	}
+
+	if (event[R1] != handled[R1]) {
+		dev_warn(chip->dev,
+			 "Unhandled event in bank1 0x%02x\n",
+			 event[R1] ^ handled[R1]);
+	}
+
+	/* DA9121_REG_SYS_EVENT_2 */
+	if (!(mask[R2] & DA9121_MASK_SYS_MASK_2_M_GPIO2) &&
+	    (event[R2] & DA9121_MASK_SYS_EVENT_2_E_GPIO2)) {
+		dev_warn(chip->dev, "Handled E_GPIO2\n");
+		handled[R2] |= DA9121_MASK_SYS_EVENT_2_E_GPIO2;
+		ret = IRQ_HANDLED;
+	}
+
+	if (!(mask[R2] & DA9121_MASK_SYS_MASK_2_M_GPIO1) &&
+	    (event[R2] & DA9121_MASK_SYS_EVENT_2_E_GPIO1)) {
+		dev_warn(chip->dev, "Handled E_GPIO1\n");
+		handled[R2] |= DA9121_MASK_SYS_EVENT_2_E_GPIO1;
+		ret = IRQ_HANDLED;
+	}
+
+	if (!(mask[R2] & DA9121_MASK_SYS_MASK_2_M_GPIO0) &&
+	    (event[R2] & DA9121_MASK_SYS_EVENT_2_E_GPIO0)) {
+		dev_warn(chip->dev, "Handled E_GPIO0\n");
+		handled[R2] |= DA9121_MASK_SYS_EVENT_2_E_GPIO0;
+		ret = IRQ_HANDLED;
+	}
+
+	if (event[R2] != handled[R2]) {
+		dev_warn(chip->dev,
+			 "Unhandled event in bank2 0x%02x\n",
+			 event[R2] ^ handled[R2]);
+	}
+
+	/* Mask the interrupts for persistent events OV, OC, UV, WARN, CRIT */
+	for (i = R0; i < REG_MAX_NUM-1; i++) {
+		if (handled[i]) {
+			unsigned int reg = DA9121_REG_SYS_MASK_0 + i;
+			unsigned int mbit = handled[i];
+
+			err = regmap_update_bits(chip->regmap, reg, mbit, mbit);
+			if (err < 0) {
+				dev_err(chip->dev,
+					"Failed to mask 0x%02x interrupt %d\n",
+					reg, err);
+				ret = IRQ_NONE;
+				goto error;
+			}
+		}
+	}
+
+	/* clear the events */
+	if (handled[R0] | handled[R1] | handled[R2]) {
+		err = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_EVENT_0,
+				(const void *)handled, (size_t)REG_MAX_NUM);
+		if (err < 0) {
+			dev_err(chip->dev, "Fail to write EVENTs %d\n", err);
+			ret = IRQ_NONE;
+			goto error;
+		}
+	}
+
+	queue_delayed_work(system_freezable_wq, &chip->work, 0);
+error:
+	return ret;
+}
+
 static int da9121_set_regulator_config(struct da9121 *chip)
 {
 	struct regulator_config config = { };
@@ -835,6 +1245,117 @@ static int da9121_assign_chip_model(struct i2c_client *i2c,
 	return ret;
 }
 
+static int da9121_set_irq_masks(struct da9121 *chip, bool mask_irqs)
+{
+	unsigned int mask0, update0;
+	unsigned int mask1, update1;
+	unsigned int mask3;
+	int ret = 0;
+
+	if (chip->chip_irq != 0) {
+		mask0 = DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT |
+			DA9121_MASK_SYS_MASK_0_M_TEMP_WARN;
+
+		mask1 = DA9121_MASK_SYS_MASK_1_M_OV1 |
+			DA9121_MASK_SYS_MASK_1_M_UV1 |
+			DA9121_MASK_SYS_MASK_1_M_OC1;
+
+		if (mask_irqs) {
+			update0 = mask0;
+			update1 = mask1;
+		} else {
+			update0 = 0;
+			update1 = 0;
+		}
+
+		ret = regmap_update_bits(chip->regmap,
+				DA9121_REG_SYS_MASK_0,
+				mask0,
+				update0);
+		if (ret < 0) {
+			dev_err(chip->dev, "Failed to write MASK 0 reg %d\n",
+				ret);
+			goto error;
+		}
+
+		ret = regmap_update_bits(chip->regmap,
+				DA9121_REG_SYS_MASK_1,
+				mask1,
+				update1);
+		if (ret < 0) {
+			dev_err(chip->dev, "Failed to write MASK 1 reg %d\n",
+				ret);
+			goto error;
+		}
+
+		/* permanently disable IRQs for VR_HOT and PG1_STAT */
+		mask3 = DA9121_MASK_SYS_MASK_3_M_VR_HOT |
+			DA9121_MASK_SYS_MASK_3_M_PG1_STAT;
+
+		ret = regmap_update_bits(chip->regmap,
+				DA9121_REG_SYS_MASK_3,
+				mask3,
+				mask3);
+		if (ret < 0) {
+			dev_err(chip->dev, "Failed to write MASK 3 reg %d\n",
+			ret);
+			goto error;
+		}
+	}
+
+error:
+	return ret;
+}
+
+static int da9121_config_irq(struct i2c_client *i2c,
+			struct da9121 *chip)
+{
+	unsigned int p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS;
+	int ret = 0;
+
+	chip->chip_irq = i2c->irq;
+
+	if (chip->chip_irq != 0) {
+		if (!of_property_read_u32(chip->dev->of_node,
+					  "dlg,irq-polling-delay-passive",
+					  &p_delay)) {
+			if (p_delay < DA9121_MIN_POLLING_PERIOD_MS ||
+			    p_delay > DA9121_MAX_POLLING_PERIOD_MS) {
+				dev_warn(chip->dev,
+					 "Out-of-range polling period %d ms\n",
+					 p_delay);
+				p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS;
+			}
+		}
+
+		chip->passive_delay = p_delay;
+
+		ret = devm_request_threaded_irq(chip->dev,
+					chip->chip_irq, NULL,
+					da9121_irq_handler,
+					IRQF_TRIGGER_LOW|IRQF_ONESHOT,
+					"da9121", chip);
+		if (ret != 0) {
+			dev_err(chip->dev, "Failed IRQ request: %d\n",
+				chip->chip_irq);
+			goto error;
+		}
+
+		ret = da9121_set_irq_masks(chip, false);
+		if (ret != 0) {
+			dev_err(chip->dev, "Failed to set IRQ masks: %d\n",
+				ret);
+			goto error;
+		}
+
+		INIT_DELAYED_WORK(&chip->work, da9121_status_poll_on);
+		dev_info(chip->dev, "Interrupt polling period set at %d ms\n",
+			 chip->passive_delay);
+	}
+error:
+	return ret;
+}
+
 static const struct of_device_id da9121_dt_ids[] = {
 	{ .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 },
 	{ .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 },
@@ -877,6 +1398,12 @@ static int da9121_i2c_probe(struct i2c_client *i2c,
 	if (ret < 0)
 		goto error;
 
+	ret = da9121_set_irq_masks(chip, true);
+	if (ret != 0) {
+		dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret);
+		goto error;
+	}
+
 	if (!chip->pdata)
 		chip->pdata = da9121_parse_regulators_dt(chip);
 
@@ -889,6 +1416,26 @@ static int da9121_i2c_probe(struct i2c_client *i2c,
 	if (ret < 0)
 		goto error;
 
+	ret = da9121_config_irq(i2c, chip);
+	if (ret < 0)
+		goto error;
+
+error:
+	return ret;
+}
+
+static int da9121_i2c_remove(struct i2c_client *i2c)
+{
+	struct da9121 *chip = i2c_get_clientdata(i2c);
+	int ret = 0;
+
+	ret = da9121_set_irq_masks(chip, true);
+	if (ret != 0) {
+		dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret);
+		goto error;
+	}
+
+	cancel_delayed_work(&chip->work);
 error:
 	return ret;
 }
@@ -911,6 +1458,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c,
 		.of_match_table = of_match_ptr(da9121_dt_ids),
 	},
 	.probe = da9121_i2c_probe,
+	.remove = da9121_i2c_remove,
 	.id_table = da9121_i2c_id,
 };
 
-- 
1.9.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ