diff options
author | Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> | 2021-06-03 08:41:55 +0300 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2021-06-21 13:08:40 +0100 |
commit | 7111c6d1b31b42c8c758f6681e895a5116e3bad6 (patch) | |
tree | 8335c7f51220aaec7686dacb67c9be8831bad121 /include | |
parent | 157d2230193ae683fcffcc1cd0a2c3aa4479955f (diff) | |
download | linux-7111c6d1b31b42c8c758f6681e895a5116e3bad6.tar.bz2 |
regulator: IRQ based event/error notification helpers
Provide helper function for IC's implementing regulator notifications
when an IRQ fires. The helper also works for IRQs which can not be acked.
Helper can be set to disable the IRQ at handler and then re-enabling it
on delayed work later. The helper also adds regulator_get_error_flags()
errors in cache for the duration of IRQ disabling.
Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Link: https://lore.kernel.org/r/ebdf86d8c22b924667ec2385330e30fcbfac0119.1622628334.git.matti.vaittinen@fi.rohmeurope.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'include')
-rw-r--r-- | include/linux/regulator/driver.h | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 7ec0fa79d1a8..1d1a8951e740 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -413,6 +413,128 @@ struct regulator_config { struct gpio_desc *ena_gpiod; }; +/** + * struct regulator_err_state - regulator error/notification status + * + * @rdev: Regulator which status the struct indicates. + * @notifs: Events which have occurred on the regulator. + * @errors: Errors which are active on the regulator. + * @possible_errs: Errors which can be signaled (by given IRQ). + */ +struct regulator_err_state { + struct regulator_dev *rdev; + unsigned long notifs; + unsigned long errors; + int possible_errs; +}; + +/** + * struct regulator_irq_data - regulator error/notification status date + * + * @states: Status structs for each of the associated regulators. + * @num_states: Amount of associated regulators. + * @data: Driver data pointer given at regulator_irq_desc. + * @opaque: Value storage for IC driver. Core does not update this. ICs + * may want to store status register value here at map_event and + * compare contents at 'renable' callback to see if new problems + * have been added to status. If that is the case it may be + * desirable to return REGULATOR_ERROR_CLEARED and not + * REGULATOR_ERROR_ON to allow IRQ fire again and to generate + * notifications also for the new issues. + * + * This structure is passed to 'map_event' and 'renable' callbacks for + * reporting regulator status to core. + */ +struct regulator_irq_data { + struct regulator_err_state *states; + int num_states; + void *data; + long opaque; +}; + +/** + * struct regulator_irq_desc - notification sender for IRQ based events. + * + * @name: The visible name for the IRQ + * @fatal_cnt: If this IRQ is used to signal HW damaging condition it may be + * best to shut-down regulator(s) or reboot the SOC if error + * handling is repeatedly failing. If fatal_cnt is given the IRQ + * handling is aborted if it fails for fatal_cnt times and die() + * callback (if populated) or BUG() is called to try to prevent + * further damage. + * @reread_ms: The time which is waited before attempting to re-read status + * at the worker if IC reading fails. Immediate re-read is done + * if time is not specified. + * @irq_off_ms: The time which IRQ is kept disabled before re-evaluating the + * status for devices which keep IRQ disabled for duration of the + * error. If this is not given the IRQ is left enabled and renable + * is not called. + * @skip_off: If set to true the IRQ handler will attempt to check if any of + * the associated regulators are enabled prior to taking other + * actions. If no regulators are enabled and this is set to true + * a spurious IRQ is assumed and IRQ_NONE is returned. + * @high_prio: Boolean to indicate that high priority WQ should be used. + * @data: Driver private data pointer which will be passed as such to + * the renable, map_event and die callbacks in regulator_irq_data. + * @die: Protection callback. If IC status reading or recovery actions + * fail fatal_cnt times this callback or BUG() is called. This + * callback should implement a final protection attempt like + * disabling the regulator. If protection succeeded this may + * return 0. If anything else is returned the core assumes final + * protection failed and calls BUG() as a last resort. + * @map_event: Driver callback to map IRQ status into regulator devices with + * events / errors. NOTE: callback MUST initialize both the + * errors and notifs for all rdevs which it signals having + * active events as core does not clean the map data. + * REGULATOR_FAILED_RETRY can be returned to indicate that the + * status reading from IC failed. If this is repeated for + * fatal_cnt times the core will call die() callback or BUG() + * as a last resort to protect the HW. + * @renable: Optional callback to check status (if HW supports that) before + * re-enabling IRQ. If implemented this should clear the error + * flags so that errors fetched by regulator_get_error_flags() + * are updated. If callback is not implemented then errors are + * assumed to be cleared and IRQ is re-enabled. + * REGULATOR_FAILED_RETRY can be returned to + * indicate that the status reading from IC failed. If this is + * repeated for 'fatal_cnt' times the core will call die() + * callback or BUG() as a last resort to protect the HW. + * Returning zero indicates that the problem in HW has been solved + * and IRQ will be re-enabled. Returning REGULATOR_ERROR_ON + * indicates the error condition is still active and keeps IRQ + * disabled. Please note that returning REGULATOR_ERROR_ON does + * not retrigger evaluating what events are active or resending + * notifications. If this is needed you probably want to return + * zero and allow IRQ to retrigger causing events to be + * re-evaluated and re-sent. + * + * This structure is used for registering regulator IRQ notification helper. + */ +struct regulator_irq_desc { + const char *name; + int irq_flags; + int fatal_cnt; + int reread_ms; + int irq_off_ms; + bool skip_off; + bool high_prio; + void *data; + + int (*die)(struct regulator_irq_data *rid); + int (*map_event)(int irq, struct regulator_irq_data *rid, + unsigned long *dev_mask); + int (*renable)(struct regulator_irq_data *rid); +}; + +/* + * Return values for regulator IRQ helpers. + */ +enum { + REGULATOR_ERROR_CLEARED, + REGULATOR_FAILED_RETRY, + REGULATOR_ERROR_ON, +}; + /* * struct coupling_desc * @@ -477,6 +599,9 @@ struct regulator_dev { /* time when this regulator was disabled last time */ ktime_t last_off; + int cached_err; + bool use_cached_err; + spinlock_t err_lock; }; struct regulator_dev * @@ -491,6 +616,16 @@ void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev); int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); +void *devm_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); +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); +void regulator_irq_helper_cancel(void **handle); void *rdev_get_drvdata(struct regulator_dev *rdev); struct device *rdev_get_dev(struct regulator_dev *rdev); |