lkml.org 
[lkml]   [2020]   [Nov]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH V3 10/10] 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: Adam Ward <Adam.Ward.opensource@diasemi.com>
    ---
    drivers/regulator/da9121-regulator.c | 286 +++++++++++++++++++++++++++++++++++
    1 file changed, 286 insertions(+)

    diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c
    index 8e50f55..d9a8a4b 100644
    --- a/drivers/regulator/da9121-regulator.c
    +++ b/drivers/regulator/da9121-regulator.c
    @@ -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 @@ struct da9121_field {
    { 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 int da9121_of_parse_cb(struct device_node *np,
    [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 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
    \
     
     \ /
      Last update: 2020-11-30 18:09    [W:3.093 / U:0.084 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site