Unverified Commit 40bb5b02 authored by Adam Ward's avatar Adam Ward Committed by Mark Brown
Browse files

regulator: da9121: add interrupt support



Adds interrupt handler for variants, and notifications for events; over
temperature/voltage/current. Because the IRQs are triggered by persisting
status, they must be masked and the status polled until clear, before the
IRQ can be enabled again.

Signed-off-by: default avatarAdam Ward <Adam.Ward.opensource@diasemi.com>
Link: https://lore.kernel.org/r/fe21796bbcbadff84a472a4cc581ae8fafc7f8f5.1606755367.git.Adam.Ward.opensource@diasemi.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 65ac9704
Loading
Loading
Loading
Loading
+286 −0
Original line number Diff line number Diff line
@@ -23,15 +23,21 @@
#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 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;
};

@@ -106,6 +112,59 @@ static const struct da9121_field da9121_mode_field[2] = {
	{ DA9xxx_REG_BUCK_BUCK2_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE },
};

struct status_event_data {
	int buck_id; /* 0=core, 1/2-buck */
	int reg_index;  /* index for status/event/mask register selection */
	int status_bit; /* bit masks... */
	int event_bit;
	int mask_bit;
	unsigned long notification; /* Notification for status inception */
	char *warn; /* if NULL, notify - otherwise dev_warn this string */
};

#define DA9121_STATUS(id, bank, name, notification, warning) \
	{ id, bank, \
	DA9121_MASK_SYS_STATUS_##bank##_##name, \
	DA9121_MASK_SYS_EVENT_##bank##_E_##name, \
	DA9121_MASK_SYS_MASK_##bank##_M_##name, \
	notification, warning }

/* For second buck related event bits that are specific to DA9122, DA9220 variants */
#define DA9xxx_STATUS(id, bank, name, notification, warning) \
	{ id, bank, \
	DA9xxx_MASK_SYS_STATUS_##bank##_##name, \
	DA9xxx_MASK_SYS_EVENT_##bank##_E_##name, \
	DA9xxx_MASK_SYS_MASK_##bank##_M_##name, \
	notification, warning }

/* The status signals that may need servicing, depending on device variant.
 * After assertion, they persist; so event is notified, the IRQ disabled,
 * and status polled until clear again and IRQ is reenabled.
 *
 * SG/PG1/PG2 should be set when device first powers up and should never
 * re-occur. When this driver starts, it is expected that these will have
 * self-cleared for when the IRQs are enabled, so these should never be seen.
 * If seen, the implication is that the device has reset.
 *
 * GPIO0/1/2 are not configured for use by default, so should not be seen.
 */
const struct status_event_data status_event_handling[] = {
	DA9xxx_STATUS(0, 0, SG, 0, "Handled E_SG\n"),
	DA9121_STATUS(0, 0, TEMP_CRIT, (REGULATOR_EVENT_OVER_TEMP|REGULATOR_EVENT_DISABLE), NULL),
	DA9121_STATUS(0, 0, TEMP_WARN, REGULATOR_EVENT_OVER_TEMP, NULL),
	DA9121_STATUS(1, 1, PG1, 0, "Handled E_PG1\n"),
	DA9121_STATUS(1, 1, OV1, REGULATOR_EVENT_REGULATION_OUT, NULL),
	DA9121_STATUS(1, 1, UV1, REGULATOR_EVENT_UNDER_VOLTAGE, NULL),
	DA9121_STATUS(1, 1, OC1, REGULATOR_EVENT_OVER_CURRENT, NULL),
	DA9xxx_STATUS(2, 1, PG2, 0, "Handled E_PG2\n"),
	DA9xxx_STATUS(2, 1, OV2, REGULATOR_EVENT_REGULATION_OUT, NULL),
	DA9xxx_STATUS(2, 1, UV2, REGULATOR_EVENT_UNDER_VOLTAGE, NULL),
	DA9xxx_STATUS(2, 1, OC2, REGULATOR_EVENT_OVER_CURRENT, NULL),
	DA9121_STATUS(0, 2, GPIO0, 0, "Handled E_GPIO0\n"),
	DA9121_STATUS(0, 2, GPIO1, 0, "Handled E_GPIO1\n"),
	DA9121_STATUS(0, 2, GPIO2, 0, "Handled E_GPIO2\n"),
};

static int da9121_get_current_limit(struct regulator_dev *rdev)
{
	struct da9121 *chip = rdev_get_drvdata(rdev);
@@ -479,6 +538,157 @@ static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] =
	[DA9121_TYPE_DA9217] = { &da9217_reg, NULL },
};

static void da9121_status_poll_on(struct work_struct *work)
{
	struct da9121 *chip = container_of(work, struct da9121, work.work);
	int status[3] = {0};
	int clear[3] = {0};
	unsigned long delay;
	int i;
	int ret;

	ret = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_STATUS_0, status, 2);
	if (ret < 0) {
		dev_err(chip->dev,
			"Failed to read STATUS registers: %d\n", ret);
		goto error;
	}

	/* Possible events are tested to be within range for the variant, potentially
	 * masked by the IRQ handler (not just warned about), as having been masked,
	 * and the respective state cleared - then flagged to unmask for next IRQ.
	 */
	for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) {
		const struct status_event_data *item = &status_event_handling[i];
		int reg_idx = item->reg_index;
		bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks);
		bool supported = (item->warn == NULL);
		bool persisting = (chip->persistent[reg_idx] & item->event_bit);
		bool now_cleared = !(status[reg_idx] & item->status_bit);

		if (relevant && supported && persisting && now_cleared) {
			clear[reg_idx] |= item->mask_bit;
			chip->persistent[reg_idx] &= ~item->event_bit;
		}
	}

	for (i = 0; i < 2; 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[0] | chip->persistent[1]) {
		delay = msecs_to_jiffies(chip->passive_delay);
		queue_delayed_work(system_freezable_wq, &chip->work, delay);
	}

error:
	return;
}

static irqreturn_t da9121_irq_handler(int irq, void *data)
{
	struct da9121 *chip = data;
	struct regulator_dev *rdev;
	int event[3] = {0};
	int handled[3] = {0};
	int mask[3] = {0};
	int ret = IRQ_NONE;
	int i;
	int err;

	err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_EVENT_0, event, 3);
	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, mask, 3);
	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];

	/* Possible events are tested to be within range for the variant, currently
	 * enabled, and having triggered this IRQ. The event may then be notified,
	 * or a warning given for unexpected events - those from device POR, and
	 * currently unsupported GPIO configurations.
	 */
	for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) {
		const struct status_event_data *item = &status_event_handling[i];
		int reg_idx = item->reg_index;
		bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks);
		bool enabled = !(mask[reg_idx] & item->mask_bit);
		bool active = (event[reg_idx] & item->event_bit);
		bool notify = (item->warn == NULL);

		if (relevant && enabled && active) {
			if (notify) {
				chip->persistent[reg_idx] |= item->event_bit;
				regulator_notifier_call_chain(rdev, item->notification, NULL);
			} else {
				dev_warn(chip->dev, item->warn);
				handled[reg_idx] |= item->event_bit;
				ret = IRQ_HANDLED;
			}
		}
	}

	for (i = 0; i < 3; i++) {
		if (event[i] != handled[i]) {
			dev_warn(chip->dev,
				"Unhandled event(s) in bank%d 0x%02x\n", i,
				event[i] ^ handled[i]);
		}
	}

	/* Mask the interrupts for persistent events OV, OC, UV, WARN, CRIT */
	for (i = 0; i < 2; 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[0] | handled[1] | handled[2]) {
		err = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_EVENT_0, handled, 3);
		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 = { };
@@ -711,6 +921,56 @@ static int da9121_assign_chip_model(struct i2c_client *i2c,
	return ret;
}

static int da9121_config_irq(struct i2c_client *i2c,
			struct da9121 *chip)
{
	unsigned int p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS;
	const int mask_all[4] = { 0, 0, 0xFF, 0xFF };
	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-ms",
					  &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 = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4);
		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 },
@@ -738,6 +998,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c,
			    const struct i2c_device_id *id)
{
	struct da9121 *chip;
	const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
	int ret = 0;

	chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL);
@@ -753,12 +1014,36 @@ static int da9121_i2c_probe(struct i2c_client *i2c,
	if (ret < 0)
		goto error;

	ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4);
	if (ret != 0) {
		dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret);
		goto error;
	}

	ret = da9121_set_regulator_config(chip);
	if (ret < 0)
		goto error;

	ret = da9121_config_irq(i2c, chip);

error:
	return ret;
}

static int da9121_i2c_remove(struct i2c_client *i2c)
{
	struct da9121 *chip = i2c_get_clientdata(i2c);
	const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
	int ret = 0;

	cancel_delayed_work_sync(&chip->work);

	ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4);
	if (ret != 0)
		dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret);
	return ret;
}

static const struct i2c_device_id da9121_i2c_id[] = {
	{"da9121", DA9121_TYPE_DA9121_DA9130},
	{"da9130", DA9121_TYPE_DA9121_DA9130},
@@ -777,6 +1062,7 @@ static struct i2c_driver da9121_regulator_driver = {
		.of_match_table = of_match_ptr(da9121_dt_ids),
	},
	.probe = da9121_i2c_probe,
	.remove = da9121_i2c_remove,
	.id_table = da9121_i2c_id,
};