root/drivers/regulator/irq_helpers.c
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2021 ROHM Semiconductors
// regulator IRQ based event notification helpers
//
// Logic has been partially adapted from qcom-labibb driver.
//
// Author: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>

#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/regulator/driver.h>

#include "internal.h"

#define REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS 10000

struct regulator_irq {
        struct regulator_irq_data rdata;
        struct regulator_irq_desc desc;
        int irq;
        int retry_cnt;
        struct delayed_work isr_work;
};

/*
 * Should only be called from threaded handler to prevent potential deadlock
 */
static void rdev_flag_err(struct regulator_dev *rdev, int err)
{
        spin_lock(&rdev->err_lock);
        rdev->cached_err |= err;
        spin_unlock(&rdev->err_lock);
}

static void rdev_clear_err(struct regulator_dev *rdev, int err)
{
        spin_lock(&rdev->err_lock);
        rdev->cached_err &= ~err;
        spin_unlock(&rdev->err_lock);
}

static void regulator_notifier_isr_work(struct work_struct *work)
{
        struct regulator_irq *h;
        struct regulator_irq_desc *d;
        struct regulator_irq_data *rid;
        int ret = 0;
        int tmo, i;
        int num_rdevs;

        h = container_of(work, struct regulator_irq,
                            isr_work.work);
        d = &h->desc;
        rid = &h->rdata;
        num_rdevs = rid->num_states;

reread:
        if (d->fatal_cnt && h->retry_cnt > d->fatal_cnt) {
                if (!d->die)
                        return hw_protection_trigger("Regulator HW failure? - no IC recovery",
                                                     REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
                ret = d->die(rid);
                /*
                 * If the 'last resort' IC recovery failed we will have
                 * nothing else left to do...
                 */
                if (ret)
                        return hw_protection_trigger("Regulator HW failure. IC recovery failed",
                                                     REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);

                /*
                 * If h->die() was implemented we assume recovery has been
                 * attempted (probably regulator was shut down) and we
                 * just enable IRQ and bail-out.
                 */
                goto enable_out;
        }
        if (d->renable) {
                ret = d->renable(rid);

                if (ret == REGULATOR_FAILED_RETRY) {
                        /* Driver could not get current status */
                        h->retry_cnt++;
                        if (!d->reread_ms)
                                goto reread;

                        tmo = d->reread_ms;
                        goto reschedule;
                }

                if (ret) {
                        /*
                         * IC status reading succeeded. update error info
                         * just in case the renable changed it.
                         */
                        for (i = 0; i < num_rdevs; i++) {
                                struct regulator_err_state *stat;
                                struct regulator_dev *rdev;

                                stat = &rid->states[i];
                                rdev = stat->rdev;
                                rdev_clear_err(rdev, (~stat->errors) &
                                                      stat->possible_errs);
                        }
                        h->retry_cnt++;
                        /*
                         * The IC indicated problem is still ON - no point in
                         * re-enabling the IRQ. Retry later.
                         */
                        tmo = d->irq_off_ms;
                        goto reschedule;
                }
        }

        /*
         * Either IC reported problem cleared or no status checker was provided.
         * If problems are gone - good. If not - then the IRQ will fire again
         * and we'll have a new nice loop. In any case we should clear error
         * flags here and re-enable IRQs.
         */
        for (i = 0; i < num_rdevs; i++) {
                struct regulator_err_state *stat;
                struct regulator_dev *rdev;

                stat = &rid->states[i];
                rdev = stat->rdev;
                rdev_clear_err(rdev, stat->possible_errs);
        }

        /*
         * Things have been seemingly successful => zero retry-counter.
         */
        h->retry_cnt = 0;

enable_out:
        enable_irq(h->irq);

        return;

reschedule:
        if (!d->high_prio)
                mod_delayed_work(system_dfl_wq, &h->isr_work,
                                 msecs_to_jiffies(tmo));
        else
                mod_delayed_work(system_highpri_wq, &h->isr_work,
                                 msecs_to_jiffies(tmo));
}

static irqreturn_t regulator_notifier_isr(int irq, void *data)
{
        struct regulator_irq *h = data;
        struct regulator_irq_desc *d;
        struct regulator_irq_data *rid;
        unsigned long rdev_map = 0;
        int num_rdevs;
        int ret, i;

        d = &h->desc;
        rid = &h->rdata;
        num_rdevs = rid->num_states;

        if (d->fatal_cnt)
                h->retry_cnt++;

        /*
         * we spare a few cycles by not clearing statuses prior to this call.
         * The IC driver must initialize the status buffers for rdevs
         * which it indicates having active events via rdev_map.
         *
         * Maybe we should just to be on a safer side(?)
         */
        ret = d->map_event(irq, rid, &rdev_map);

        /*
         * If status reading fails (which is unlikely) we don't ack/disable
         * IRQ but just increase fail count and retry when IRQ fires again.
         * If retry_count exceeds the given safety limit we call IC specific die
         * handler which can try disabling regulator(s).
         *
         * If no die handler is given we will just power-off as a last resort.
         *
         * We could try disabling all associated rdevs - but we might shoot
         * ourselves in the head and leave the problematic regulator enabled. So
         * if IC has no die-handler populated we just assume the regulator
         * can't be disabled.
         */
        if (unlikely(ret == REGULATOR_FAILED_RETRY))
                goto fail_out;

        h->retry_cnt = 0;
        /*
         * Let's not disable IRQ if there were no status bits for us. We'd
         * better leave spurious IRQ handling to genirq
         */
        if (ret || !rdev_map)
                return IRQ_NONE;

        /*
         * Some events are bogus if the regulator is disabled. Skip such events
         * if all relevant regulators are disabled
         */
        if (d->skip_off) {
                for_each_set_bit(i, &rdev_map, num_rdevs) {
                        struct regulator_dev *rdev;
                        const struct regulator_ops *ops;

                        rdev = rid->states[i].rdev;
                        ops = rdev->desc->ops;

                        /*
                         * If any of the flagged regulators is enabled we do
                         * handle this
                         */
                        if (ops->is_enabled(rdev))
                                break;
                }
                if (i == num_rdevs)
                        return IRQ_NONE;
        }

        /* Disable IRQ if HW keeps line asserted */
        if (d->irq_off_ms)
                disable_irq_nosync(irq);

        /*
         * IRQ seems to be for us. Let's fire correct notifiers / store error
         * flags
         */
        for_each_set_bit(i, &rdev_map, num_rdevs) {
                struct regulator_err_state *stat;
                struct regulator_dev *rdev;

                stat = &rid->states[i];
                rdev = stat->rdev;

                rdev_dbg(rdev, "Sending regulator notification EVT 0x%lx\n",
                         stat->notifs);

                regulator_notifier_call_chain(rdev, stat->notifs, NULL);
                rdev_flag_err(rdev, stat->errors);
        }

        if (d->irq_off_ms) {
                if (!d->high_prio)
                        schedule_delayed_work(&h->isr_work,
                                              msecs_to_jiffies(d->irq_off_ms));
                else
                        mod_delayed_work(system_highpri_wq,
                                         &h->isr_work,
                                         msecs_to_jiffies(d->irq_off_ms));
        }

        return IRQ_HANDLED;

fail_out:
        if (d->fatal_cnt && h->retry_cnt > d->fatal_cnt) {
                /* If we have no recovery, just try shut down straight away */
                if (!d->die) {
                        hw_protection_trigger("Regulator failure. Retry count exceeded",
                                              REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
                } else {
                        ret = d->die(rid);
                        /* If die() failed shut down as a last attempt to save the HW */
                        if (ret)
                                hw_protection_trigger("Regulator failure. Recovery failed",
                                                      REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
                }
        }

        return IRQ_NONE;
}

static int init_rdev_state(struct device *dev, struct regulator_irq *h,
                           struct regulator_dev **rdev, int common_err,
                           int *rdev_err, int rdev_amount)
{
        int i;

        h->rdata.states = devm_kzalloc(dev, sizeof(*h->rdata.states) *
                                       rdev_amount, GFP_KERNEL);
        if (!h->rdata.states)
                return -ENOMEM;

        h->rdata.num_states = rdev_amount;
        h->rdata.data = h->desc.data;

        for (i = 0; i < rdev_amount; i++) {
                h->rdata.states[i].possible_errs = common_err;
                if (rdev_err)
                        h->rdata.states[i].possible_errs |= *rdev_err++;
                h->rdata.states[i].rdev = *rdev++;
        }

        return 0;
}

static void init_rdev_errors(struct regulator_irq *h)
{
        int i;

        for (i = 0; i < h->rdata.num_states; i++)
                if (h->rdata.states[i].possible_errs)
                        h->rdata.states[i].rdev->use_cached_err = true;
}

/**
 * regulator_irq_helper - register IRQ based regulator event/error notifier
 *
 * @dev:                device providing the IRQs
 * @d:                  IRQ helper descriptor.
 * @irq:                IRQ used to inform events/errors to be notified.
 * @irq_flags:          Extra IRQ flags to be OR'ed with the default
 *                      IRQF_ONESHOT when requesting the (threaded) irq.
 * @common_errs:        Errors which can be flagged by this IRQ for all rdevs.
 *                      When IRQ is re-enabled these errors will be cleared
 *                      from all associated regulators. Use this instead of the
 *                      per_rdev_errs if you use
 *                      regulator_irq_map_event_simple() for event mapping.
 * @per_rdev_errs:      Optional error flag array describing errors specific
 *                      for only some of the regulators. These errors will be
 *                      or'ed with common errors. If this is given the array
 *                      should contain rdev_amount flags. Can be set to NULL
 *                      if there is no regulator specific error flags for this
 *                      IRQ.
 * @rdev:               Array of pointers to regulators associated with this
 *                      IRQ.
 * @rdev_amount:        Amount of regulators associated with this IRQ.
 *
 * Return: handle to irq_helper or an ERR_PTR() encoded negative error number.
 */
void *regulator_irq_helper(struct device *dev,
                           const struct regulator_irq_desc *d, int irq,
                           int irq_flags, int common_errs, int *per_rdev_errs,
                           struct regulator_dev **rdev, int rdev_amount)
{
        struct regulator_irq *h;
        int ret;

        if (!rdev_amount || !d || !d->map_event || !d->name)
                return ERR_PTR(-EINVAL);

        h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
        if (!h)
                return ERR_PTR(-ENOMEM);

        h->irq = irq;
        h->desc = *d;
        h->desc.name = devm_kstrdup(dev, d->name, GFP_KERNEL);
        if (!h->desc.name)
                return ERR_PTR(-ENOMEM);

        ret = init_rdev_state(dev, h, rdev, common_errs, per_rdev_errs,
                              rdev_amount);
        if (ret)
                return ERR_PTR(ret);

        init_rdev_errors(h);

        if (h->desc.irq_off_ms)
                INIT_DELAYED_WORK(&h->isr_work, regulator_notifier_isr_work);

        ret = request_threaded_irq(h->irq, NULL, regulator_notifier_isr,
                                   IRQF_ONESHOT | irq_flags, h->desc.name, h);
        if (ret) {
                dev_err(dev, "Failed to request IRQ %d\n", irq);

                return ERR_PTR(ret);
        }

        return h;
}
EXPORT_SYMBOL_GPL(regulator_irq_helper);

/**
 * regulator_irq_helper_cancel - drop IRQ based regulator event/error notifier
 *
 * @handle:             Pointer to handle returned by a successful call to
 *                      regulator_irq_helper(). Will be NULLed upon return.
 *
 * The associated IRQ is released and work is cancelled when the function
 * returns.
 */
void regulator_irq_helper_cancel(void **handle)
{
        if (handle && *handle) {
                struct regulator_irq *h = *handle;

                free_irq(h->irq, h);
                if (h->desc.irq_off_ms)
                        cancel_delayed_work_sync(&h->isr_work);

                h = NULL;
        }
}
EXPORT_SYMBOL_GPL(regulator_irq_helper_cancel);

/**
 * regulator_irq_map_event_simple - regulator IRQ notification for trivial IRQs
 *
 * @irq:        Number of IRQ that occurred.
 * @rid:        Information about the event IRQ indicates.
 *              The function fills in the &regulator_err_state->notifs
 *              and &regulator_err_state->errors fields of
 *              &regulator_irq_data->states as output.
 * @dev_mask:   mask indicating the regulator originating the IRQ.
 *
 * Regulators whose IRQ has single, well defined purpose (always indicate
 * exactly one event, and are relevant to exactly one regulator device) can
 * use this function as their map_event callback for their regulator IRQ
 * notification helper. Exactly one rdev and exactly one error (in
 * "common_errs"-field) can be given at IRQ helper registration for
 * regulator_irq_map_event_simple() to be viable.
 *
 * Return: 0.
 */
int regulator_irq_map_event_simple(int irq, struct regulator_irq_data *rid,
                            unsigned long *dev_mask)
{
        int err = rid->states[0].possible_errs;

        *dev_mask = 1;
        /*
         * This helper should only be used in a situation where the IRQ
         * can indicate only one type of problem for one specific rdev.
         * Something fishy is going on if we are having multiple rdevs or ERROR
         * flags here.
         */
        if (WARN_ON(rid->num_states != 1 || hweight32(err) != 1))
                return 0;

        rid->states[0].errors = err;
        rid->states[0].notifs = regulator_err2notif(err);

        return 0;
}
EXPORT_SYMBOL_GPL(regulator_irq_map_event_simple);