17111c6d1SMatti Vaittinen // SPDX-License-Identifier: GPL-2.0
27111c6d1SMatti Vaittinen //
37111c6d1SMatti Vaittinen // Copyright (C) 2021 ROHM Semiconductors
47111c6d1SMatti Vaittinen // regulator IRQ based event notification helpers
57111c6d1SMatti Vaittinen //
67111c6d1SMatti Vaittinen // Logic has been partially adapted from qcom-labibb driver.
77111c6d1SMatti Vaittinen //
87111c6d1SMatti Vaittinen // Author: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
97111c6d1SMatti Vaittinen 
107111c6d1SMatti Vaittinen #include <linux/device.h>
117111c6d1SMatti Vaittinen #include <linux/err.h>
127111c6d1SMatti Vaittinen #include <linux/interrupt.h>
137111c6d1SMatti Vaittinen #include <linux/kernel.h>
147111c6d1SMatti Vaittinen #include <linux/reboot.h>
157111c6d1SMatti Vaittinen #include <linux/regmap.h>
167111c6d1SMatti Vaittinen #include <linux/slab.h>
177111c6d1SMatti Vaittinen #include <linux/spinlock.h>
187111c6d1SMatti Vaittinen #include <linux/regulator/driver.h>
197111c6d1SMatti Vaittinen 
207111c6d1SMatti Vaittinen #include "internal.h"
217111c6d1SMatti Vaittinen 
227111c6d1SMatti Vaittinen #define REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS 10000
237111c6d1SMatti Vaittinen 
247111c6d1SMatti Vaittinen struct regulator_irq {
257111c6d1SMatti Vaittinen 	struct regulator_irq_data rdata;
267111c6d1SMatti Vaittinen 	struct regulator_irq_desc desc;
277111c6d1SMatti Vaittinen 	int irq;
287111c6d1SMatti Vaittinen 	int retry_cnt;
297111c6d1SMatti Vaittinen 	struct delayed_work isr_work;
307111c6d1SMatti Vaittinen };
317111c6d1SMatti Vaittinen 
327111c6d1SMatti Vaittinen /*
337111c6d1SMatti Vaittinen  * Should only be called from threaded handler to prevent potential deadlock
347111c6d1SMatti Vaittinen  */
rdev_flag_err(struct regulator_dev * rdev,int err)357111c6d1SMatti Vaittinen static void rdev_flag_err(struct regulator_dev *rdev, int err)
367111c6d1SMatti Vaittinen {
377111c6d1SMatti Vaittinen 	spin_lock(&rdev->err_lock);
387111c6d1SMatti Vaittinen 	rdev->cached_err |= err;
397111c6d1SMatti Vaittinen 	spin_unlock(&rdev->err_lock);
407111c6d1SMatti Vaittinen }
417111c6d1SMatti Vaittinen 
rdev_clear_err(struct regulator_dev * rdev,int err)427111c6d1SMatti Vaittinen static void rdev_clear_err(struct regulator_dev *rdev, int err)
437111c6d1SMatti Vaittinen {
447111c6d1SMatti Vaittinen 	spin_lock(&rdev->err_lock);
457111c6d1SMatti Vaittinen 	rdev->cached_err &= ~err;
467111c6d1SMatti Vaittinen 	spin_unlock(&rdev->err_lock);
477111c6d1SMatti Vaittinen }
487111c6d1SMatti Vaittinen 
regulator_notifier_isr_work(struct work_struct * work)497111c6d1SMatti Vaittinen static void regulator_notifier_isr_work(struct work_struct *work)
507111c6d1SMatti Vaittinen {
517111c6d1SMatti Vaittinen 	struct regulator_irq *h;
527111c6d1SMatti Vaittinen 	struct regulator_irq_desc *d;
537111c6d1SMatti Vaittinen 	struct regulator_irq_data *rid;
547111c6d1SMatti Vaittinen 	int ret = 0;
557111c6d1SMatti Vaittinen 	int tmo, i;
567111c6d1SMatti Vaittinen 	int num_rdevs;
577111c6d1SMatti Vaittinen 
587111c6d1SMatti Vaittinen 	h = container_of(work, struct regulator_irq,
597111c6d1SMatti Vaittinen 			    isr_work.work);
607111c6d1SMatti Vaittinen 	d = &h->desc;
617111c6d1SMatti Vaittinen 	rid = &h->rdata;
627111c6d1SMatti Vaittinen 	num_rdevs = rid->num_states;
637111c6d1SMatti Vaittinen 
647111c6d1SMatti Vaittinen reread:
657111c6d1SMatti Vaittinen 	if (d->fatal_cnt && h->retry_cnt > d->fatal_cnt) {
667111c6d1SMatti Vaittinen 		if (!d->die)
677111c6d1SMatti Vaittinen 			return hw_protection_shutdown("Regulator HW failure? - no IC recovery",
687111c6d1SMatti Vaittinen 						      REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
697111c6d1SMatti Vaittinen 		ret = d->die(rid);
707111c6d1SMatti Vaittinen 		/*
717111c6d1SMatti Vaittinen 		 * If the 'last resort' IC recovery failed we will have
727111c6d1SMatti Vaittinen 		 * nothing else left to do...
737111c6d1SMatti Vaittinen 		 */
747111c6d1SMatti Vaittinen 		if (ret)
757111c6d1SMatti Vaittinen 			return hw_protection_shutdown("Regulator HW failure. IC recovery failed",
767111c6d1SMatti Vaittinen 						      REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
777111c6d1SMatti Vaittinen 
787111c6d1SMatti Vaittinen 		/*
797111c6d1SMatti Vaittinen 		 * If h->die() was implemented we assume recovery has been
807111c6d1SMatti Vaittinen 		 * attempted (probably regulator was shut down) and we
817111c6d1SMatti Vaittinen 		 * just enable IRQ and bail-out.
827111c6d1SMatti Vaittinen 		 */
837111c6d1SMatti Vaittinen 		goto enable_out;
847111c6d1SMatti Vaittinen 	}
857111c6d1SMatti Vaittinen 	if (d->renable) {
867111c6d1SMatti Vaittinen 		ret = d->renable(rid);
877111c6d1SMatti Vaittinen 
887111c6d1SMatti Vaittinen 		if (ret == REGULATOR_FAILED_RETRY) {
897111c6d1SMatti Vaittinen 			/* Driver could not get current status */
907111c6d1SMatti Vaittinen 			h->retry_cnt++;
917111c6d1SMatti Vaittinen 			if (!d->reread_ms)
927111c6d1SMatti Vaittinen 				goto reread;
937111c6d1SMatti Vaittinen 
947111c6d1SMatti Vaittinen 			tmo = d->reread_ms;
957111c6d1SMatti Vaittinen 			goto reschedule;
967111c6d1SMatti Vaittinen 		}
977111c6d1SMatti Vaittinen 
987111c6d1SMatti Vaittinen 		if (ret) {
997111c6d1SMatti Vaittinen 			/*
1007111c6d1SMatti Vaittinen 			 * IC status reading succeeded. update error info
1017111c6d1SMatti Vaittinen 			 * just in case the renable changed it.
1027111c6d1SMatti Vaittinen 			 */
1037111c6d1SMatti Vaittinen 			for (i = 0; i < num_rdevs; i++) {
1047111c6d1SMatti Vaittinen 				struct regulator_err_state *stat;
1057111c6d1SMatti Vaittinen 				struct regulator_dev *rdev;
1067111c6d1SMatti Vaittinen 
1077111c6d1SMatti Vaittinen 				stat = &rid->states[i];
1087111c6d1SMatti Vaittinen 				rdev = stat->rdev;
1097111c6d1SMatti Vaittinen 				rdev_clear_err(rdev, (~stat->errors) &
1107111c6d1SMatti Vaittinen 						      stat->possible_errs);
1117111c6d1SMatti Vaittinen 			}
1127111c6d1SMatti Vaittinen 			h->retry_cnt++;
1137111c6d1SMatti Vaittinen 			/*
1147111c6d1SMatti Vaittinen 			 * The IC indicated problem is still ON - no point in
1157111c6d1SMatti Vaittinen 			 * re-enabling the IRQ. Retry later.
1167111c6d1SMatti Vaittinen 			 */
1177111c6d1SMatti Vaittinen 			tmo = d->irq_off_ms;
1187111c6d1SMatti Vaittinen 			goto reschedule;
1197111c6d1SMatti Vaittinen 		}
1207111c6d1SMatti Vaittinen 	}
1217111c6d1SMatti Vaittinen 
1227111c6d1SMatti Vaittinen 	/*
1237111c6d1SMatti Vaittinen 	 * Either IC reported problem cleared or no status checker was provided.
1247111c6d1SMatti Vaittinen 	 * If problems are gone - good. If not - then the IRQ will fire again
1257111c6d1SMatti Vaittinen 	 * and we'll have a new nice loop. In any case we should clear error
1267111c6d1SMatti Vaittinen 	 * flags here and re-enable IRQs.
1277111c6d1SMatti Vaittinen 	 */
1287111c6d1SMatti Vaittinen 	for (i = 0; i < num_rdevs; i++) {
1297111c6d1SMatti Vaittinen 		struct regulator_err_state *stat;
1307111c6d1SMatti Vaittinen 		struct regulator_dev *rdev;
1317111c6d1SMatti Vaittinen 
1327111c6d1SMatti Vaittinen 		stat = &rid->states[i];
1337111c6d1SMatti Vaittinen 		rdev = stat->rdev;
1347111c6d1SMatti Vaittinen 		rdev_clear_err(rdev, stat->possible_errs);
1357111c6d1SMatti Vaittinen 	}
1367111c6d1SMatti Vaittinen 
1377111c6d1SMatti Vaittinen 	/*
1387111c6d1SMatti Vaittinen 	 * Things have been seemingly successful => zero retry-counter.
1397111c6d1SMatti Vaittinen 	 */
1407111c6d1SMatti Vaittinen 	h->retry_cnt = 0;
1417111c6d1SMatti Vaittinen 
1427111c6d1SMatti Vaittinen enable_out:
1437111c6d1SMatti Vaittinen 	enable_irq(h->irq);
1447111c6d1SMatti Vaittinen 
1457111c6d1SMatti Vaittinen 	return;
1467111c6d1SMatti Vaittinen 
1477111c6d1SMatti Vaittinen reschedule:
1487111c6d1SMatti Vaittinen 	if (!d->high_prio)
1497111c6d1SMatti Vaittinen 		mod_delayed_work(system_wq, &h->isr_work,
1507111c6d1SMatti Vaittinen 				 msecs_to_jiffies(tmo));
1517111c6d1SMatti Vaittinen 	else
1527111c6d1SMatti Vaittinen 		mod_delayed_work(system_highpri_wq, &h->isr_work,
1537111c6d1SMatti Vaittinen 				 msecs_to_jiffies(tmo));
1547111c6d1SMatti Vaittinen }
1557111c6d1SMatti Vaittinen 
regulator_notifier_isr(int irq,void * data)1567111c6d1SMatti Vaittinen static irqreturn_t regulator_notifier_isr(int irq, void *data)
1577111c6d1SMatti Vaittinen {
1587111c6d1SMatti Vaittinen 	struct regulator_irq *h = data;
1597111c6d1SMatti Vaittinen 	struct regulator_irq_desc *d;
1607111c6d1SMatti Vaittinen 	struct regulator_irq_data *rid;
1617111c6d1SMatti Vaittinen 	unsigned long rdev_map = 0;
1627111c6d1SMatti Vaittinen 	int num_rdevs;
1637111c6d1SMatti Vaittinen 	int ret, i;
1647111c6d1SMatti Vaittinen 
1657111c6d1SMatti Vaittinen 	d = &h->desc;
1667111c6d1SMatti Vaittinen 	rid = &h->rdata;
1677111c6d1SMatti Vaittinen 	num_rdevs = rid->num_states;
1687111c6d1SMatti Vaittinen 
1697111c6d1SMatti Vaittinen 	if (d->fatal_cnt)
1707111c6d1SMatti Vaittinen 		h->retry_cnt++;
1717111c6d1SMatti Vaittinen 
1727111c6d1SMatti Vaittinen 	/*
1737111c6d1SMatti Vaittinen 	 * we spare a few cycles by not clearing statuses prior to this call.
1747111c6d1SMatti Vaittinen 	 * The IC driver must initialize the status buffers for rdevs
1757111c6d1SMatti Vaittinen 	 * which it indicates having active events via rdev_map.
1767111c6d1SMatti Vaittinen 	 *
1777111c6d1SMatti Vaittinen 	 * Maybe we should just to be on a safer side(?)
1787111c6d1SMatti Vaittinen 	 */
1797111c6d1SMatti Vaittinen 	ret = d->map_event(irq, rid, &rdev_map);
1807111c6d1SMatti Vaittinen 
1817111c6d1SMatti Vaittinen 	/*
1827111c6d1SMatti Vaittinen 	 * If status reading fails (which is unlikely) we don't ack/disable
1837111c6d1SMatti Vaittinen 	 * IRQ but just increase fail count and retry when IRQ fires again.
1847111c6d1SMatti Vaittinen 	 * If retry_count exceeds the given safety limit we call IC specific die
1857111c6d1SMatti Vaittinen 	 * handler which can try disabling regulator(s).
1867111c6d1SMatti Vaittinen 	 *
187ad3ead1eSMatti Vaittinen 	 * If no die handler is given we will just power-off as a last resort.
1887111c6d1SMatti Vaittinen 	 *
1897111c6d1SMatti Vaittinen 	 * We could try disabling all associated rdevs - but we might shoot
1907111c6d1SMatti Vaittinen 	 * ourselves in the head and leave the problematic regulator enabled. So
1917111c6d1SMatti Vaittinen 	 * if IC has no die-handler populated we just assume the regulator
1927111c6d1SMatti Vaittinen 	 * can't be disabled.
1937111c6d1SMatti Vaittinen 	 */
1947111c6d1SMatti Vaittinen 	if (unlikely(ret == REGULATOR_FAILED_RETRY))
1957111c6d1SMatti Vaittinen 		goto fail_out;
1967111c6d1SMatti Vaittinen 
1977111c6d1SMatti Vaittinen 	h->retry_cnt = 0;
1987111c6d1SMatti Vaittinen 	/*
1997111c6d1SMatti Vaittinen 	 * Let's not disable IRQ if there were no status bits for us. We'd
2007111c6d1SMatti Vaittinen 	 * better leave spurious IRQ handling to genirq
2017111c6d1SMatti Vaittinen 	 */
2027111c6d1SMatti Vaittinen 	if (ret || !rdev_map)
2037111c6d1SMatti Vaittinen 		return IRQ_NONE;
2047111c6d1SMatti Vaittinen 
2057111c6d1SMatti Vaittinen 	/*
2067111c6d1SMatti Vaittinen 	 * Some events are bogus if the regulator is disabled. Skip such events
2077111c6d1SMatti Vaittinen 	 * if all relevant regulators are disabled
2087111c6d1SMatti Vaittinen 	 */
2097111c6d1SMatti Vaittinen 	if (d->skip_off) {
2107111c6d1SMatti Vaittinen 		for_each_set_bit(i, &rdev_map, num_rdevs) {
2117111c6d1SMatti Vaittinen 			struct regulator_dev *rdev;
2127111c6d1SMatti Vaittinen 			const struct regulator_ops *ops;
2137111c6d1SMatti Vaittinen 
2147111c6d1SMatti Vaittinen 			rdev = rid->states[i].rdev;
2157111c6d1SMatti Vaittinen 			ops = rdev->desc->ops;
2167111c6d1SMatti Vaittinen 
2177111c6d1SMatti Vaittinen 			/*
2187111c6d1SMatti Vaittinen 			 * If any of the flagged regulators is enabled we do
2197111c6d1SMatti Vaittinen 			 * handle this
2207111c6d1SMatti Vaittinen 			 */
2217111c6d1SMatti Vaittinen 			if (ops->is_enabled(rdev))
2227111c6d1SMatti Vaittinen 				break;
2237111c6d1SMatti Vaittinen 		}
2247111c6d1SMatti Vaittinen 		if (i == num_rdevs)
2257111c6d1SMatti Vaittinen 			return IRQ_NONE;
2267111c6d1SMatti Vaittinen 	}
2277111c6d1SMatti Vaittinen 
2287111c6d1SMatti Vaittinen 	/* Disable IRQ if HW keeps line asserted */
2297111c6d1SMatti Vaittinen 	if (d->irq_off_ms)
2307111c6d1SMatti Vaittinen 		disable_irq_nosync(irq);
2317111c6d1SMatti Vaittinen 
2327111c6d1SMatti Vaittinen 	/*
2337111c6d1SMatti Vaittinen 	 * IRQ seems to be for us. Let's fire correct notifiers / store error
2347111c6d1SMatti Vaittinen 	 * flags
2357111c6d1SMatti Vaittinen 	 */
2367111c6d1SMatti Vaittinen 	for_each_set_bit(i, &rdev_map, num_rdevs) {
2377111c6d1SMatti Vaittinen 		struct regulator_err_state *stat;
2387111c6d1SMatti Vaittinen 		struct regulator_dev *rdev;
2397111c6d1SMatti Vaittinen 
2407111c6d1SMatti Vaittinen 		stat = &rid->states[i];
2417111c6d1SMatti Vaittinen 		rdev = stat->rdev;
2427111c6d1SMatti Vaittinen 
2437111c6d1SMatti Vaittinen 		rdev_dbg(rdev, "Sending regulator notification EVT 0x%lx\n",
2447111c6d1SMatti Vaittinen 			 stat->notifs);
2457111c6d1SMatti Vaittinen 
2467111c6d1SMatti Vaittinen 		regulator_notifier_call_chain(rdev, stat->notifs, NULL);
2477111c6d1SMatti Vaittinen 		rdev_flag_err(rdev, stat->errors);
2487111c6d1SMatti Vaittinen 	}
2497111c6d1SMatti Vaittinen 
2507111c6d1SMatti Vaittinen 	if (d->irq_off_ms) {
2517111c6d1SMatti Vaittinen 		if (!d->high_prio)
2527111c6d1SMatti Vaittinen 			schedule_delayed_work(&h->isr_work,
2537111c6d1SMatti Vaittinen 					      msecs_to_jiffies(d->irq_off_ms));
2547111c6d1SMatti Vaittinen 		else
2557111c6d1SMatti Vaittinen 			mod_delayed_work(system_highpri_wq,
2567111c6d1SMatti Vaittinen 					 &h->isr_work,
2577111c6d1SMatti Vaittinen 					 msecs_to_jiffies(d->irq_off_ms));
2587111c6d1SMatti Vaittinen 	}
2597111c6d1SMatti Vaittinen 
2607111c6d1SMatti Vaittinen 	return IRQ_HANDLED;
2617111c6d1SMatti Vaittinen 
2627111c6d1SMatti Vaittinen fail_out:
2637111c6d1SMatti Vaittinen 	if (d->fatal_cnt && h->retry_cnt > d->fatal_cnt) {
2647111c6d1SMatti Vaittinen 		/* If we have no recovery, just try shut down straight away */
2657111c6d1SMatti Vaittinen 		if (!d->die) {
2667111c6d1SMatti Vaittinen 			hw_protection_shutdown("Regulator failure. Retry count exceeded",
2677111c6d1SMatti Vaittinen 					       REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
2687111c6d1SMatti Vaittinen 		} else {
2697111c6d1SMatti Vaittinen 			ret = d->die(rid);
2707111c6d1SMatti Vaittinen 			/* If die() failed shut down as a last attempt to save the HW */
2717111c6d1SMatti Vaittinen 			if (ret)
2727111c6d1SMatti Vaittinen 				hw_protection_shutdown("Regulator failure. Recovery failed",
2737111c6d1SMatti Vaittinen 						       REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
2747111c6d1SMatti Vaittinen 		}
2757111c6d1SMatti Vaittinen 	}
2767111c6d1SMatti Vaittinen 
2777111c6d1SMatti Vaittinen 	return IRQ_NONE;
2787111c6d1SMatti Vaittinen }
2797111c6d1SMatti Vaittinen 
init_rdev_state(struct device * dev,struct regulator_irq * h,struct regulator_dev ** rdev,int common_err,int * rdev_err,int rdev_amount)2807111c6d1SMatti Vaittinen static int init_rdev_state(struct device *dev, struct regulator_irq *h,
2817111c6d1SMatti Vaittinen 			   struct regulator_dev **rdev, int common_err,
2827111c6d1SMatti Vaittinen 			   int *rdev_err, int rdev_amount)
2837111c6d1SMatti Vaittinen {
2847111c6d1SMatti Vaittinen 	int i;
2857111c6d1SMatti Vaittinen 
2867111c6d1SMatti Vaittinen 	h->rdata.states = devm_kzalloc(dev, sizeof(*h->rdata.states) *
2877111c6d1SMatti Vaittinen 				       rdev_amount, GFP_KERNEL);
2887111c6d1SMatti Vaittinen 	if (!h->rdata.states)
2897111c6d1SMatti Vaittinen 		return -ENOMEM;
2907111c6d1SMatti Vaittinen 
2917111c6d1SMatti Vaittinen 	h->rdata.num_states = rdev_amount;
2927111c6d1SMatti Vaittinen 	h->rdata.data = h->desc.data;
2937111c6d1SMatti Vaittinen 
2947111c6d1SMatti Vaittinen 	for (i = 0; i < rdev_amount; i++) {
2957111c6d1SMatti Vaittinen 		h->rdata.states[i].possible_errs = common_err;
2967111c6d1SMatti Vaittinen 		if (rdev_err)
2977111c6d1SMatti Vaittinen 			h->rdata.states[i].possible_errs |= *rdev_err++;
2987111c6d1SMatti Vaittinen 		h->rdata.states[i].rdev = *rdev++;
2997111c6d1SMatti Vaittinen 	}
3007111c6d1SMatti Vaittinen 
3017111c6d1SMatti Vaittinen 	return 0;
3027111c6d1SMatti Vaittinen }
3037111c6d1SMatti Vaittinen 
init_rdev_errors(struct regulator_irq * h)3047111c6d1SMatti Vaittinen static void init_rdev_errors(struct regulator_irq *h)
3057111c6d1SMatti Vaittinen {
3067111c6d1SMatti Vaittinen 	int i;
3077111c6d1SMatti Vaittinen 
3087111c6d1SMatti Vaittinen 	for (i = 0; i < h->rdata.num_states; i++)
3097111c6d1SMatti Vaittinen 		if (h->rdata.states[i].possible_errs)
3107111c6d1SMatti Vaittinen 			h->rdata.states[i].rdev->use_cached_err = true;
3117111c6d1SMatti Vaittinen }
3127111c6d1SMatti Vaittinen 
3137111c6d1SMatti Vaittinen /**
3147111c6d1SMatti Vaittinen  * regulator_irq_helper - register IRQ based regulator event/error notifier
3157111c6d1SMatti Vaittinen  *
3167111c6d1SMatti Vaittinen  * @dev:		device providing the IRQs
3177111c6d1SMatti Vaittinen  * @d:			IRQ helper descriptor.
3187111c6d1SMatti Vaittinen  * @irq:		IRQ used to inform events/errors to be notified.
3197111c6d1SMatti Vaittinen  * @irq_flags:		Extra IRQ flags to be OR'ed with the default
3207111c6d1SMatti Vaittinen  *			IRQF_ONESHOT when requesting the (threaded) irq.
3217111c6d1SMatti Vaittinen  * @common_errs:	Errors which can be flagged by this IRQ for all rdevs.
3227111c6d1SMatti Vaittinen  *			When IRQ is re-enabled these errors will be cleared
323a764ff77SMatti Vaittinen  *			from all associated regulators. Use this instead of the
324a764ff77SMatti Vaittinen  *			per_rdev_errs if you use
325a764ff77SMatti Vaittinen  *			regulator_irq_map_event_simple() for event mapping.
3267111c6d1SMatti Vaittinen  * @per_rdev_errs:	Optional error flag array describing errors specific
3277111c6d1SMatti Vaittinen  *			for only some of the regulators. These errors will be
3287111c6d1SMatti Vaittinen  *			or'ed with common errors. If this is given the array
3297111c6d1SMatti Vaittinen  *			should contain rdev_amount flags. Can be set to NULL
3307111c6d1SMatti Vaittinen  *			if there is no regulator specific error flags for this
3317111c6d1SMatti Vaittinen  *			IRQ.
3327111c6d1SMatti Vaittinen  * @rdev:		Array of pointers to regulators associated with this
3337111c6d1SMatti Vaittinen  *			IRQ.
3347111c6d1SMatti Vaittinen  * @rdev_amount:	Amount of regulators associated with this IRQ.
3357111c6d1SMatti Vaittinen  *
3367111c6d1SMatti Vaittinen  * Return: handle to irq_helper or an ERR_PTR() encoded error code.
3377111c6d1SMatti Vaittinen  */
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)3387111c6d1SMatti Vaittinen void *regulator_irq_helper(struct device *dev,
3397111c6d1SMatti Vaittinen 			   const struct regulator_irq_desc *d, int irq,
3407111c6d1SMatti Vaittinen 			   int irq_flags, int common_errs, int *per_rdev_errs,
3417111c6d1SMatti Vaittinen 			   struct regulator_dev **rdev, int rdev_amount)
3427111c6d1SMatti Vaittinen {
3437111c6d1SMatti Vaittinen 	struct regulator_irq *h;
3447111c6d1SMatti Vaittinen 	int ret;
3457111c6d1SMatti Vaittinen 
3467111c6d1SMatti Vaittinen 	if (!rdev_amount || !d || !d->map_event || !d->name)
3477111c6d1SMatti Vaittinen 		return ERR_PTR(-EINVAL);
3487111c6d1SMatti Vaittinen 
3497111c6d1SMatti Vaittinen 	h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
3507111c6d1SMatti Vaittinen 	if (!h)
3517111c6d1SMatti Vaittinen 		return ERR_PTR(-ENOMEM);
3527111c6d1SMatti Vaittinen 
3537111c6d1SMatti Vaittinen 	h->irq = irq;
3547111c6d1SMatti Vaittinen 	h->desc = *d;
3553ad4d29bSMatti Vaittinen 	h->desc.name = devm_kstrdup(dev, d->name, GFP_KERNEL);
3563ad4d29bSMatti Vaittinen 	if (!h->desc.name)
3573ad4d29bSMatti Vaittinen 		return ERR_PTR(-ENOMEM);
3587111c6d1SMatti Vaittinen 
3597111c6d1SMatti Vaittinen 	ret = init_rdev_state(dev, h, rdev, common_errs, per_rdev_errs,
3607111c6d1SMatti Vaittinen 			      rdev_amount);
3617111c6d1SMatti Vaittinen 	if (ret)
3627111c6d1SMatti Vaittinen 		return ERR_PTR(ret);
3637111c6d1SMatti Vaittinen 
3647111c6d1SMatti Vaittinen 	init_rdev_errors(h);
3657111c6d1SMatti Vaittinen 
3667111c6d1SMatti Vaittinen 	if (h->desc.irq_off_ms)
3677111c6d1SMatti Vaittinen 		INIT_DELAYED_WORK(&h->isr_work, regulator_notifier_isr_work);
3687111c6d1SMatti Vaittinen 
3697111c6d1SMatti Vaittinen 	ret = request_threaded_irq(h->irq, NULL, regulator_notifier_isr,
3707111c6d1SMatti Vaittinen 				   IRQF_ONESHOT | irq_flags, h->desc.name, h);
3717111c6d1SMatti Vaittinen 	if (ret) {
3727111c6d1SMatti Vaittinen 		dev_err(dev, "Failed to request IRQ %d\n", irq);
3737111c6d1SMatti Vaittinen 
3747111c6d1SMatti Vaittinen 		return ERR_PTR(ret);
3757111c6d1SMatti Vaittinen 	}
3767111c6d1SMatti Vaittinen 
3777111c6d1SMatti Vaittinen 	return h;
3787111c6d1SMatti Vaittinen }
3797111c6d1SMatti Vaittinen EXPORT_SYMBOL_GPL(regulator_irq_helper);
3807111c6d1SMatti Vaittinen 
3817111c6d1SMatti Vaittinen /**
3827111c6d1SMatti Vaittinen  * regulator_irq_helper_cancel - drop IRQ based regulator event/error notifier
3837111c6d1SMatti Vaittinen  *
3847111c6d1SMatti Vaittinen  * @handle:		Pointer to handle returned by a successful call to
3857111c6d1SMatti Vaittinen  *			regulator_irq_helper(). Will be NULLed upon return.
3867111c6d1SMatti Vaittinen  *
3877111c6d1SMatti Vaittinen  * The associated IRQ is released and work is cancelled when the function
3887111c6d1SMatti Vaittinen  * returns.
3897111c6d1SMatti Vaittinen  */
regulator_irq_helper_cancel(void ** handle)3907111c6d1SMatti Vaittinen void regulator_irq_helper_cancel(void **handle)
3917111c6d1SMatti Vaittinen {
3927111c6d1SMatti Vaittinen 	if (handle && *handle) {
3937111c6d1SMatti Vaittinen 		struct regulator_irq *h = *handle;
3947111c6d1SMatti Vaittinen 
3957111c6d1SMatti Vaittinen 		free_irq(h->irq, h);
3967111c6d1SMatti Vaittinen 		if (h->desc.irq_off_ms)
3977111c6d1SMatti Vaittinen 			cancel_delayed_work_sync(&h->isr_work);
3987111c6d1SMatti Vaittinen 
3997111c6d1SMatti Vaittinen 		h = NULL;
4007111c6d1SMatti Vaittinen 	}
4017111c6d1SMatti Vaittinen }
4027111c6d1SMatti Vaittinen EXPORT_SYMBOL_GPL(regulator_irq_helper_cancel);
403a764ff77SMatti Vaittinen 
404a764ff77SMatti Vaittinen /**
405a764ff77SMatti Vaittinen  * regulator_irq_map_event_simple - regulator IRQ notification for trivial IRQs
406a764ff77SMatti Vaittinen  *
407a764ff77SMatti Vaittinen  * @irq:	Number of IRQ that occurred
408a764ff77SMatti Vaittinen  * @rid:	Information about the event IRQ indicates
409a764ff77SMatti Vaittinen  * @dev_mask:	mask indicating the regulator originating the IRQ
410a764ff77SMatti Vaittinen  *
411a764ff77SMatti Vaittinen  * Regulators whose IRQ has single, well defined purpose (always indicate
412a764ff77SMatti Vaittinen  * exactly one event, and are relevant to exactly one regulator device) can
413a764ff77SMatti Vaittinen  * use this function as their map_event callbac for their regulator IRQ
414a764ff77SMatti Vaittinen  * notification helperk. Exactly one rdev and exactly one error (in
415a764ff77SMatti Vaittinen  * "common_errs"-field) can be given at IRQ helper registration for
416a764ff77SMatti Vaittinen  * regulator_irq_map_event_simple() to be viable.
417a764ff77SMatti Vaittinen  */
regulator_irq_map_event_simple(int irq,struct regulator_irq_data * rid,unsigned long * dev_mask)418a764ff77SMatti Vaittinen int regulator_irq_map_event_simple(int irq, struct regulator_irq_data *rid,
419a764ff77SMatti Vaittinen 			    unsigned long *dev_mask)
420a764ff77SMatti Vaittinen {
421a764ff77SMatti Vaittinen 	int err = rid->states[0].possible_errs;
422a764ff77SMatti Vaittinen 
423a764ff77SMatti Vaittinen 	*dev_mask = 1;
424a764ff77SMatti Vaittinen 	/*
425a764ff77SMatti Vaittinen 	 * This helper should only be used in a situation where the IRQ
426a764ff77SMatti Vaittinen 	 * can indicate only one type of problem for one specific rdev.
427a764ff77SMatti Vaittinen 	 * Something fishy is going on if we are having multiple rdevs or ERROR
428a764ff77SMatti Vaittinen 	 * flags here.
429a764ff77SMatti Vaittinen 	 */
430a764ff77SMatti Vaittinen 	if (WARN_ON(rid->num_states != 1 || hweight32(err) != 1))
431a764ff77SMatti Vaittinen 		return 0;
432a764ff77SMatti Vaittinen 
433a764ff77SMatti Vaittinen 	rid->states[0].errors = err;
434a764ff77SMatti Vaittinen 	rid->states[0].notifs = regulator_err2notif(err);
435a764ff77SMatti Vaittinen 
436a764ff77SMatti Vaittinen 	return 0;
437a764ff77SMatti Vaittinen }
438a764ff77SMatti Vaittinen EXPORT_SYMBOL_GPL(regulator_irq_map_event_simple);
439a764ff77SMatti Vaittinen 
440