···11+# SPDX-License-Identifier: GPL-2.0-only22+33+menuconfig POWER_SEQUENCING44+ tristate "Power Sequencing support"55+ help66+ Say Y here to enable the Power Sequencing subsystem.77+88+ This subsystem is designed to control power to devices that share99+ complex resources and/or require specific power sequences to be run1010+ during power-up.1111+1212+ If unsure, say no.1313+1414+if POWER_SEQUENCING1515+1616+config POWER_SEQUENCING_QCOM_WCN1717+ tristate "Qualcomm WCN family PMU driver"1818+ default m if ARCH_QCOM1919+ help2020+ Say Y here to enable the power sequencing driver for Qualcomm2121+ WCN Bluetooth/WLAN chipsets.2222+2323+ Typically, a package from the Qualcomm WCN family contains the BT2424+ and WLAN modules whose power is controlled by the PMU module. As the2525+ former two share the power-up sequence which is executed by the PMU,2626+ this driver is needed for correct power control or else we'd risk not2727+ respecting the required delays between enabling Bluetooth and WLAN.2828+2929+endif
···11+// SPDX-License-Identifier: GPL-2.0-only22+/*33+ * Copyright (C) 2024 Linaro Ltd.44+ */55+66+#include <linux/bug.h>77+#include <linux/cleanup.h>88+#include <linux/debugfs.h>99+#include <linux/device.h>1010+#include <linux/err.h>1111+#include <linux/export.h>1212+#include <linux/idr.h>1313+#include <linux/kernel.h>1414+#include <linux/kref.h>1515+#include <linux/list.h>1616+#include <linux/lockdep.h>1717+#include <linux/module.h>1818+#include <linux/mutex.h>1919+#include <linux/property.h>2020+#include <linux/pwrseq/consumer.h>2121+#include <linux/pwrseq/provider.h>2222+#include <linux/radix-tree.h>2323+#include <linux/rwsem.h>2424+#include <linux/slab.h>2525+2626+/*2727+ * Power-sequencing framework for linux.2828+ *2929+ * This subsystem allows power sequence providers to register a set of targets3030+ * that consumers may request and power-up/down.3131+ *3232+ * Glossary:3333+ *3434+ * Unit - a unit is a discreet chunk of a power sequence. For instance one unit3535+ * may enable a set of regulators, another may enable a specific GPIO. Units3636+ * can define dependencies in the form of other units that must be enabled3737+ * before it itself can be.3838+ *3939+ * Target - a target is a set of units (composed of the "final" unit and its4040+ * dependencies) that a consumer selects by its name when requesting a handle4141+ * to the power sequencer. Via the dependency system, multiple targets may4242+ * share the same parts of a power sequence but ignore parts that are4343+ * irrelevant.4444+ *4545+ * Descriptor - a handle passed by the pwrseq core to every consumer that4646+ * serves as the entry point to the provider layer. It ensures coherence4747+ * between different users and keeps reference counting consistent.4848+ *4949+ * Each provider must define a .match() callback whose role is to determine5050+ * whether a potential consumer is in fact associated with this sequencer.5151+ * This allows creating abstraction layers on top of regular device-tree5252+ * resources like regulators, clocks and other nodes connected to the consumer5353+ * via phandle.5454+ */5555+5656+static DEFINE_IDA(pwrseq_ida);5757+5858+/*5959+ * Protects the device list on the pwrseq bus from concurrent modifications6060+ * but allows simultaneous read-only access.6161+ */6262+static DECLARE_RWSEM(pwrseq_sem);6363+6464+/**6565+ * struct pwrseq_unit - Private power-sequence unit data.6666+ * @ref: Reference count for this object. When it goes to 0, the object is6767+ * destroyed.6868+ * @name: Name of this target.6969+ * @list: Link to siblings on the list of all units of a single sequencer.7070+ * @deps: List of units on which this unit depends.7171+ * @enable: Callback running the part of the power-on sequence provided by7272+ * this unit.7373+ * @disable: Callback running the part of the power-off sequence provided7474+ * by this unit.7575+ * @enable_count: Current number of users that enabled this unit. May be the7676+ * consumer of the power sequencer or other units that depend7777+ * on this one.7878+ */7979+struct pwrseq_unit {8080+ struct kref ref;8181+ const char *name;8282+ struct list_head list;8383+ struct list_head deps;8484+ pwrseq_power_state_func enable;8585+ pwrseq_power_state_func disable;8686+ unsigned int enable_count;8787+};8888+8989+static struct pwrseq_unit *pwrseq_unit_new(const struct pwrseq_unit_data *data)9090+{9191+ struct pwrseq_unit *unit;9292+9393+ unit = kzalloc(sizeof(*unit), GFP_KERNEL);9494+ if (!unit)9595+ return NULL;9696+9797+ unit->name = kstrdup_const(data->name, GFP_KERNEL);9898+ if (!unit->name) {9999+ kfree(unit);100100+ return NULL;101101+ }102102+103103+ kref_init(&unit->ref);104104+ INIT_LIST_HEAD(&unit->deps);105105+ unit->enable = data->enable;106106+ unit->disable = data->disable;107107+108108+ return unit;109109+}110110+111111+static struct pwrseq_unit *pwrseq_unit_get(struct pwrseq_unit *unit)112112+{113113+ kref_get(&unit->ref);114114+115115+ return unit;116116+}117117+118118+static void pwrseq_unit_release(struct kref *ref);119119+120120+static void pwrseq_unit_put(struct pwrseq_unit *unit)121121+{122122+ kref_put(&unit->ref, pwrseq_unit_release);123123+}124124+125125+/**126126+ * struct pwrseq_unit_dep - Wrapper around a reference to the unit structure127127+ * allowing to keep it on multiple dependency lists128128+ * in different units.129129+ * @list: Siblings on the list.130130+ * @unit: Address of the referenced unit.131131+ */132132+struct pwrseq_unit_dep {133133+ struct list_head list;134134+ struct pwrseq_unit *unit;135135+};136136+137137+static struct pwrseq_unit_dep *pwrseq_unit_dep_new(struct pwrseq_unit *unit)138138+{139139+ struct pwrseq_unit_dep *dep;140140+141141+ dep = kzalloc(sizeof(*dep), GFP_KERNEL);142142+ if (!dep)143143+ return NULL;144144+145145+ dep->unit = unit;146146+147147+ return dep;148148+}149149+150150+static void pwrseq_unit_dep_free(struct pwrseq_unit_dep *ref)151151+{152152+ pwrseq_unit_put(ref->unit);153153+ kfree(ref);154154+}155155+156156+static void pwrseq_unit_free_deps(struct list_head *list)157157+{158158+ struct pwrseq_unit_dep *dep, *next;159159+160160+ list_for_each_entry_safe(dep, next, list, list) {161161+ list_del(&dep->list);162162+ pwrseq_unit_dep_free(dep);163163+ }164164+}165165+166166+static void pwrseq_unit_release(struct kref *ref)167167+{168168+ struct pwrseq_unit *unit = container_of(ref, struct pwrseq_unit, ref);169169+170170+ pwrseq_unit_free_deps(&unit->deps);171171+ list_del(&unit->list);172172+ kfree_const(unit->name);173173+ kfree(unit);174174+}175175+176176+/**177177+ * struct pwrseq_target - Private power-sequence target data.178178+ * @list: Siblings on the list of all targets exposed by a power sequencer.179179+ * @name: Name of the target.180180+ * @unit: Final unit for this target.181181+ * @post_enable: Callback run after the target unit has been enabled, *after*182182+ * the state lock has been released. It's useful for implementing183183+ * boot-up delays without blocking other users from powering up184184+ * using the same power sequencer.185185+ */186186+struct pwrseq_target {187187+ struct list_head list;188188+ const char *name;189189+ struct pwrseq_unit *unit;190190+ pwrseq_power_state_func post_enable;191191+};192192+193193+static struct pwrseq_target *194194+pwrseq_target_new(const struct pwrseq_target_data *data)195195+{196196+ struct pwrseq_target *target;197197+198198+ target = kzalloc(sizeof(*target), GFP_KERNEL);199199+ if (!target)200200+ return NULL;201201+202202+ target->name = kstrdup_const(data->name, GFP_KERNEL);203203+ if (!target->name) {204204+ kfree(target);205205+ return NULL;206206+ }207207+208208+ target->post_enable = data->post_enable;209209+210210+ return target;211211+}212212+213213+static void pwrseq_target_free(struct pwrseq_target *target)214214+{215215+ pwrseq_unit_put(target->unit);216216+ kfree_const(target->name);217217+ kfree(target);218218+}219219+220220+/**221221+ * struct pwrseq_device - Private power sequencing data.222222+ * @dev: Device struct associated with this sequencer.223223+ * @id: Device ID.224224+ * @owner: Prevents removal of active power sequencing providers.225225+ * @rw_lock: Protects the device from being unregistered while in use.226226+ * @state_lock: Prevents multiple users running the power sequence at the same227227+ * time.228228+ * @match: Power sequencer matching callback.229229+ * @targets: List of targets exposed by this sequencer.230230+ * @units: List of all units supported by this sequencer.231231+ */232232+struct pwrseq_device {233233+ struct device dev;234234+ int id;235235+ struct module *owner;236236+ struct rw_semaphore rw_lock;237237+ struct mutex state_lock;238238+ pwrseq_match_func match;239239+ struct list_head targets;240240+ struct list_head units;241241+};242242+243243+static struct pwrseq_device *to_pwrseq_device(struct device *dev)244244+{245245+ return container_of(dev, struct pwrseq_device, dev);246246+}247247+248248+static struct pwrseq_device *pwrseq_device_get(struct pwrseq_device *pwrseq)249249+{250250+ get_device(&pwrseq->dev);251251+252252+ return pwrseq;253253+}254254+255255+static void pwrseq_device_put(struct pwrseq_device *pwrseq)256256+{257257+ put_device(&pwrseq->dev);258258+}259259+260260+/**261261+ * struct pwrseq_desc - Wraps access to the pwrseq_device and ensures that one262262+ * user cannot break the reference counting for others.263263+ * @pwrseq: Reference to the power sequencing device.264264+ * @target: Reference to the target this descriptor allows to control.265265+ * @powered_on: Power state set by the holder of the descriptor (not necessarily266266+ * corresponding to the actual power state of the device).267267+ */268268+struct pwrseq_desc {269269+ struct pwrseq_device *pwrseq;270270+ struct pwrseq_target *target;271271+ bool powered_on;272272+};273273+274274+static const struct bus_type pwrseq_bus = {275275+ .name = "pwrseq",276276+};277277+278278+static void pwrseq_release(struct device *dev)279279+{280280+ struct pwrseq_device *pwrseq = to_pwrseq_device(dev);281281+ struct pwrseq_target *target, *pos;282282+283283+ list_for_each_entry_safe(target, pos, &pwrseq->targets, list) {284284+ list_del(&target->list);285285+ pwrseq_target_free(target);286286+ }287287+288288+ mutex_destroy(&pwrseq->state_lock);289289+ ida_free(&pwrseq_ida, pwrseq->id);290290+ kfree(pwrseq);291291+}292292+293293+static const struct device_type pwrseq_device_type = {294294+ .name = "power_sequencer",295295+ .release = pwrseq_release,296296+};297297+298298+static int pwrseq_check_unit_deps(const struct pwrseq_unit_data *data,299299+ struct radix_tree_root *visited_units)300300+{301301+ const struct pwrseq_unit_data *tmp, **cur;302302+ int ret;303303+304304+ ret = radix_tree_insert(visited_units, (unsigned long)data,305305+ (void *)data);306306+ if (ret)307307+ return ret;308308+309309+ for (cur = data->deps; cur && *cur; cur++) {310310+ tmp = radix_tree_lookup(visited_units, (unsigned long)*cur);311311+ if (tmp) {312312+ WARN(1, "Circular dependency in power sequencing flow detected!\n");313313+ return -EINVAL;314314+ }315315+316316+ ret = pwrseq_check_unit_deps(*cur, visited_units);317317+ if (ret)318318+ return ret;319319+ }320320+321321+ return 0;322322+}323323+324324+static int pwrseq_check_target_deps(const struct pwrseq_target_data *data)325325+{326326+ struct radix_tree_root visited_units;327327+ struct radix_tree_iter iter;328328+ void __rcu **slot;329329+ int ret;330330+331331+ if (!data->unit)332332+ return -EINVAL;333333+334334+ INIT_RADIX_TREE(&visited_units, GFP_KERNEL);335335+ ret = pwrseq_check_unit_deps(data->unit, &visited_units);336336+ radix_tree_for_each_slot(slot, &visited_units, &iter, 0)337337+ radix_tree_delete(&visited_units, iter.index);338338+339339+ return ret;340340+}341341+342342+static int pwrseq_unit_setup_deps(const struct pwrseq_unit_data **data,343343+ struct list_head *dep_list,344344+ struct list_head *unit_list,345345+ struct radix_tree_root *processed_units);346346+347347+static struct pwrseq_unit *348348+pwrseq_unit_setup(const struct pwrseq_unit_data *data,349349+ struct list_head *unit_list,350350+ struct radix_tree_root *processed_units)351351+{352352+ struct pwrseq_unit *unit;353353+ int ret;354354+355355+ unit = radix_tree_lookup(processed_units, (unsigned long)data);356356+ if (unit)357357+ return pwrseq_unit_get(unit);358358+359359+ unit = pwrseq_unit_new(data);360360+ if (!unit)361361+ return ERR_PTR(-ENOMEM);362362+363363+ if (data->deps) {364364+ ret = pwrseq_unit_setup_deps(data->deps, &unit->deps,365365+ unit_list, processed_units);366366+ if (ret) {367367+ pwrseq_unit_put(unit);368368+ return ERR_PTR(ret);369369+ }370370+ }371371+372372+ ret = radix_tree_insert(processed_units, (unsigned long)data, unit);373373+ if (ret) {374374+ pwrseq_unit_put(unit);375375+ return ERR_PTR(ret);376376+ }377377+378378+ list_add_tail(&unit->list, unit_list);379379+380380+ return unit;381381+}382382+383383+static int pwrseq_unit_setup_deps(const struct pwrseq_unit_data **data,384384+ struct list_head *dep_list,385385+ struct list_head *unit_list,386386+ struct radix_tree_root *processed_units)387387+{388388+ const struct pwrseq_unit_data *pos;389389+ struct pwrseq_unit_dep *dep;390390+ struct pwrseq_unit *unit;391391+ int i;392392+393393+ for (i = 0; data[i]; i++) {394394+ pos = data[i];395395+396396+ unit = pwrseq_unit_setup(pos, unit_list, processed_units);397397+ if (IS_ERR(unit))398398+ return PTR_ERR(unit);399399+400400+ dep = pwrseq_unit_dep_new(unit);401401+ if (!dep) {402402+ pwrseq_unit_put(unit);403403+ return -ENOMEM;404404+ }405405+406406+ list_add_tail(&dep->list, dep_list);407407+ }408408+409409+ return 0;410410+}411411+412412+static int pwrseq_do_setup_targets(const struct pwrseq_target_data **data,413413+ struct pwrseq_device *pwrseq,414414+ struct radix_tree_root *processed_units)415415+{416416+ const struct pwrseq_target_data *pos;417417+ struct pwrseq_target *target;418418+ int ret, i;419419+420420+ for (i = 0; data[i]; i++) {421421+ pos = data[i];422422+423423+ ret = pwrseq_check_target_deps(pos);424424+ if (ret)425425+ return ret;426426+427427+ target = pwrseq_target_new(pos);428428+ if (!target)429429+ return -ENOMEM;430430+431431+ target->unit = pwrseq_unit_setup(pos->unit, &pwrseq->units,432432+ processed_units);433433+ if (IS_ERR(target->unit)) {434434+ ret = PTR_ERR(target->unit);435435+ pwrseq_target_free(target);436436+ return ret;437437+ }438438+439439+ list_add_tail(&target->list, &pwrseq->targets);440440+ }441441+442442+ return 0;443443+}444444+445445+static int pwrseq_setup_targets(const struct pwrseq_target_data **targets,446446+ struct pwrseq_device *pwrseq)447447+{448448+ struct radix_tree_root processed_units;449449+ struct radix_tree_iter iter;450450+ void __rcu **slot;451451+ int ret;452452+453453+ INIT_RADIX_TREE(&processed_units, GFP_KERNEL);454454+ ret = pwrseq_do_setup_targets(targets, pwrseq, &processed_units);455455+ radix_tree_for_each_slot(slot, &processed_units, &iter, 0)456456+ radix_tree_delete(&processed_units, iter.index);457457+458458+ return ret;459459+}460460+461461+/**462462+ * pwrseq_device_register() - Register a new power sequencer.463463+ * @config: Configuration of the new power sequencing device.464464+ *465465+ * The config structure is only used during the call and can be freed after466466+ * the function returns. The config structure *must* have the parent device467467+ * as well as the match() callback and at least one target set.468468+ *469469+ * Returns:470470+ * Returns the address of the new pwrseq device or ERR_PTR() on failure.471471+ */472472+struct pwrseq_device *473473+pwrseq_device_register(const struct pwrseq_config *config)474474+{475475+ struct pwrseq_device *pwrseq;476476+ int ret, id;477477+478478+ if (!config->parent || !config->match || !config->targets ||479479+ !config->targets[0])480480+ return ERR_PTR(-EINVAL);481481+482482+ pwrseq = kzalloc(sizeof(*pwrseq), GFP_KERNEL);483483+ if (!pwrseq)484484+ return ERR_PTR(-ENOMEM);485485+486486+ pwrseq->dev.type = &pwrseq_device_type;487487+ pwrseq->dev.bus = &pwrseq_bus;488488+ pwrseq->dev.parent = config->parent;489489+ device_set_node(&pwrseq->dev, dev_fwnode(config->parent));490490+ dev_set_drvdata(&pwrseq->dev, config->drvdata);491491+492492+ id = ida_alloc(&pwrseq_ida, GFP_KERNEL);493493+ if (id < 0) {494494+ kfree(pwrseq);495495+ return ERR_PTR(id);496496+ }497497+498498+ pwrseq->id = id;499499+500500+ /*501501+ * From this point onwards the device's release() callback is502502+ * responsible for freeing resources.503503+ */504504+ device_initialize(&pwrseq->dev);505505+506506+ ret = dev_set_name(&pwrseq->dev, "pwrseq.%d", pwrseq->id);507507+ if (ret)508508+ goto err_put_pwrseq;509509+510510+ pwrseq->owner = config->owner ?: THIS_MODULE;511511+ pwrseq->match = config->match;512512+513513+ init_rwsem(&pwrseq->rw_lock);514514+ mutex_init(&pwrseq->state_lock);515515+ INIT_LIST_HEAD(&pwrseq->targets);516516+ INIT_LIST_HEAD(&pwrseq->units);517517+518518+ ret = pwrseq_setup_targets(config->targets, pwrseq);519519+ if (ret)520520+ goto err_put_pwrseq;521521+522522+ scoped_guard(rwsem_write, &pwrseq_sem) {523523+ ret = device_add(&pwrseq->dev);524524+ if (ret)525525+ goto err_put_pwrseq;526526+ }527527+528528+ return pwrseq;529529+530530+err_put_pwrseq:531531+ pwrseq_device_put(pwrseq);532532+ return ERR_PTR(ret);533533+}534534+EXPORT_SYMBOL_GPL(pwrseq_device_register);535535+536536+/**537537+ * pwrseq_device_unregister() - Unregister the power sequencer.538538+ * @pwrseq: Power sequencer to unregister.539539+ */540540+void pwrseq_device_unregister(struct pwrseq_device *pwrseq)541541+{542542+ struct device *dev = &pwrseq->dev;543543+ struct pwrseq_target *target;544544+545545+ scoped_guard(mutex, &pwrseq->state_lock) {546546+ guard(rwsem_write)(&pwrseq->rw_lock);547547+548548+ list_for_each_entry(target, &pwrseq->targets, list)549549+ WARN(target->unit->enable_count,550550+ "REMOVING POWER SEQUENCER WITH ACTIVE USERS\n");551551+552552+ guard(rwsem_write)(&pwrseq_sem);553553+554554+ device_del(dev);555555+ }556556+557557+ pwrseq_device_put(pwrseq);558558+}559559+EXPORT_SYMBOL_GPL(pwrseq_device_unregister);560560+561561+static void devm_pwrseq_device_unregister(void *data)562562+{563563+ struct pwrseq_device *pwrseq = data;564564+565565+ pwrseq_device_unregister(pwrseq);566566+}567567+568568+/**569569+ * devm_pwrseq_device_register() - Managed variant of pwrseq_device_register().570570+ * @dev: Managing device.571571+ * @config: Configuration of the new power sequencing device.572572+ *573573+ * Returns:574574+ * Returns the address of the new pwrseq device or ERR_PTR() on failure.575575+ */576576+struct pwrseq_device *577577+devm_pwrseq_device_register(struct device *dev,578578+ const struct pwrseq_config *config)579579+{580580+ struct pwrseq_device *pwrseq;581581+ int ret;582582+583583+ pwrseq = pwrseq_device_register(config);584584+ if (IS_ERR(pwrseq))585585+ return pwrseq;586586+587587+ ret = devm_add_action_or_reset(dev, devm_pwrseq_device_unregister,588588+ pwrseq);589589+ if (ret)590590+ return ERR_PTR(ret);591591+592592+ return pwrseq;593593+}594594+EXPORT_SYMBOL_GPL(devm_pwrseq_device_register);595595+596596+/**597597+ * pwrseq_device_get_drvdata() - Get the driver private data associated with598598+ * this sequencer.599599+ * @pwrseq: Power sequencer object.600600+ *601601+ * Returns:602602+ * Address of the private driver data.603603+ */604604+void *pwrseq_device_get_drvdata(struct pwrseq_device *pwrseq)605605+{606606+ return dev_get_drvdata(&pwrseq->dev);607607+}608608+EXPORT_SYMBOL_GPL(pwrseq_device_get_drvdata);609609+610610+struct pwrseq_match_data {611611+ struct pwrseq_desc *desc;612612+ struct device *dev;613613+ const char *target;614614+};615615+616616+static int pwrseq_match_device(struct device *pwrseq_dev, void *data)617617+{618618+ struct pwrseq_device *pwrseq = to_pwrseq_device(pwrseq_dev);619619+ struct pwrseq_match_data *match_data = data;620620+ struct pwrseq_target *target;621621+ int ret;622622+623623+ lockdep_assert_held_read(&pwrseq_sem);624624+625625+ guard(rwsem_read)(&pwrseq->rw_lock);626626+ if (!device_is_registered(&pwrseq->dev))627627+ return 0;628628+629629+ ret = pwrseq->match(pwrseq, match_data->dev);630630+ if (ret <= 0)631631+ return ret;632632+633633+ /* We got the matching device, let's find the right target. */634634+ list_for_each_entry(target, &pwrseq->targets, list) {635635+ if (strcmp(target->name, match_data->target))636636+ continue;637637+638638+ match_data->desc->target = target;639639+ }640640+641641+ /*642642+ * This device does not have this target. No point in deferring as it643643+ * will not get a new target dynamically later.644644+ */645645+ if (!match_data->desc->target)646646+ return -ENOENT;647647+648648+ if (!try_module_get(pwrseq->owner))649649+ return -EPROBE_DEFER;650650+651651+ match_data->desc->pwrseq = pwrseq_device_get(pwrseq);652652+653653+ return 1;654654+}655655+656656+/**657657+ * pwrseq_get() - Get the power sequencer associated with this device.658658+ * @dev: Device for which to get the sequencer.659659+ * @target: Name of the target exposed by the sequencer this device wants to660660+ * reach.661661+ *662662+ * Returns:663663+ * New power sequencer descriptor for use by the consumer driver or ERR_PTR()664664+ * on failure.665665+ */666666+struct pwrseq_desc *pwrseq_get(struct device *dev, const char *target)667667+{668668+ struct pwrseq_match_data match_data;669669+ int ret;670670+671671+ struct pwrseq_desc *desc __free(kfree) = kzalloc(sizeof(*desc),672672+ GFP_KERNEL);673673+ if (!desc)674674+ return ERR_PTR(-ENOMEM);675675+676676+ match_data.desc = desc;677677+ match_data.dev = dev;678678+ match_data.target = target;679679+680680+ guard(rwsem_read)(&pwrseq_sem);681681+682682+ ret = bus_for_each_dev(&pwrseq_bus, NULL, &match_data,683683+ pwrseq_match_device);684684+ if (ret < 0)685685+ return ERR_PTR(ret);686686+ if (ret == 0)687687+ /* No device matched. */688688+ return ERR_PTR(-EPROBE_DEFER);689689+690690+ return no_free_ptr(desc);691691+}692692+EXPORT_SYMBOL_GPL(pwrseq_get);693693+694694+/**695695+ * pwrseq_put() - Release the power sequencer descriptor.696696+ * @desc: Descriptor to release.697697+ */698698+void pwrseq_put(struct pwrseq_desc *desc)699699+{700700+ struct pwrseq_device *pwrseq;701701+702702+ if (!desc)703703+ return;704704+705705+ pwrseq = desc->pwrseq;706706+707707+ if (desc->powered_on)708708+ pwrseq_power_off(desc);709709+710710+ kfree(desc);711711+ module_put(pwrseq->owner);712712+ pwrseq_device_put(pwrseq);713713+}714714+EXPORT_SYMBOL_GPL(pwrseq_put);715715+716716+static void devm_pwrseq_put(void *data)717717+{718718+ struct pwrseq_desc *desc = data;719719+720720+ pwrseq_put(desc);721721+}722722+723723+/**724724+ * devm_pwrseq_get() - Managed variant of pwrseq_get().725725+ * @dev: Device for which to get the sequencer and which also manages its726726+ * lifetime.727727+ * @target: Name of the target exposed by the sequencer this device wants to728728+ * reach.729729+ *730730+ * Returns:731731+ * New power sequencer descriptor for use by the consumer driver or ERR_PTR()732732+ * on failure.733733+ */734734+struct pwrseq_desc *devm_pwrseq_get(struct device *dev, const char *target)735735+{736736+ struct pwrseq_desc *desc;737737+ int ret;738738+739739+ desc = pwrseq_get(dev, target);740740+ if (IS_ERR(desc))741741+ return desc;742742+743743+ ret = devm_add_action_or_reset(dev, devm_pwrseq_put, desc);744744+ if (ret)745745+ return ERR_PTR(ret);746746+747747+ return desc;748748+}749749+EXPORT_SYMBOL_GPL(devm_pwrseq_get);750750+751751+static int pwrseq_unit_enable(struct pwrseq_device *pwrseq,752752+ struct pwrseq_unit *target);753753+static int pwrseq_unit_disable(struct pwrseq_device *pwrseq,754754+ struct pwrseq_unit *target);755755+756756+static int pwrseq_unit_enable_deps(struct pwrseq_device *pwrseq,757757+ struct list_head *list)758758+{759759+ struct pwrseq_unit_dep *pos;760760+ int ret = 0;761761+762762+ list_for_each_entry(pos, list, list) {763763+ ret = pwrseq_unit_enable(pwrseq, pos->unit);764764+ if (ret) {765765+ list_for_each_entry_continue_reverse(pos, list, list)766766+ pwrseq_unit_disable(pwrseq, pos->unit);767767+ break;768768+ }769769+ }770770+771771+ return ret;772772+}773773+774774+static int pwrseq_unit_disable_deps(struct pwrseq_device *pwrseq,775775+ struct list_head *list)776776+{777777+ struct pwrseq_unit_dep *pos;778778+ int ret = 0;779779+780780+ list_for_each_entry_reverse(pos, list, list) {781781+ ret = pwrseq_unit_disable(pwrseq, pos->unit);782782+ if (ret) {783783+ list_for_each_entry_continue(pos, list, list)784784+ pwrseq_unit_enable(pwrseq, pos->unit);785785+ break;786786+ }787787+ }788788+789789+ return ret;790790+}791791+792792+static int pwrseq_unit_enable(struct pwrseq_device *pwrseq,793793+ struct pwrseq_unit *unit)794794+{795795+ int ret;796796+797797+ lockdep_assert_held_read(&pwrseq->rw_lock);798798+ lockdep_assert_held(&pwrseq->state_lock);799799+800800+ if (unit->enable_count != 0) {801801+ unit->enable_count++;802802+ return 0;803803+ }804804+805805+ ret = pwrseq_unit_enable_deps(pwrseq, &unit->deps);806806+ if (ret) {807807+ dev_err(&pwrseq->dev,808808+ "Failed to enable dependencies before power-on for target '%s': %d\n",809809+ unit->name, ret);810810+ return ret;811811+ }812812+813813+ if (unit->enable) {814814+ ret = unit->enable(pwrseq);815815+ if (ret) {816816+ dev_err(&pwrseq->dev,817817+ "Failed to enable target '%s': %d\n",818818+ unit->name, ret);819819+ pwrseq_unit_disable_deps(pwrseq, &unit->deps);820820+ return ret;821821+ }822822+ }823823+824824+ unit->enable_count++;825825+826826+ return 0;827827+}828828+829829+static int pwrseq_unit_disable(struct pwrseq_device *pwrseq,830830+ struct pwrseq_unit *unit)831831+{832832+ int ret;833833+834834+ lockdep_assert_held_read(&pwrseq->rw_lock);835835+ lockdep_assert_held(&pwrseq->state_lock);836836+837837+ if (unit->enable_count == 0) {838838+ WARN(1, "Unmatched power-off for target '%s'\n",839839+ unit->name);840840+ return -EBUSY;841841+ }842842+843843+ if (unit->enable_count != 1) {844844+ unit->enable_count--;845845+ return 0;846846+ }847847+848848+ if (unit->disable) {849849+ ret = unit->disable(pwrseq);850850+ if (ret) {851851+ dev_err(&pwrseq->dev,852852+ "Failed to disable target '%s': %d\n",853853+ unit->name, ret);854854+ return ret;855855+ }856856+ }857857+858858+ ret = pwrseq_unit_disable_deps(pwrseq, &unit->deps);859859+ if (ret) {860860+ dev_err(&pwrseq->dev,861861+ "Failed to disable dependencies after power-off for target '%s': %d\n",862862+ unit->name, ret);863863+ if (unit->enable)864864+ unit->enable(pwrseq);865865+ return ret;866866+ }867867+868868+ unit->enable_count--;869869+870870+ return 0;871871+}872872+873873+/**874874+ * pwrseq_power_on() - Issue a power-on request on behalf of the consumer875875+ * device.876876+ * @desc: Descriptor referencing the power sequencer.877877+ *878878+ * This function tells the power sequencer that the consumer wants to be879879+ * powered-up. The sequencer may already have powered-up the device in which880880+ * case the function returns 0. If the power-up sequence is already in881881+ * progress, the function will block until it's done and return 0. If this is882882+ * the first request, the device will be powered up.883883+ *884884+ * Returns:885885+ * 0 on success, negative error number on failure.886886+ */887887+int pwrseq_power_on(struct pwrseq_desc *desc)888888+{889889+ struct pwrseq_device *pwrseq;890890+ struct pwrseq_target *target;891891+ struct pwrseq_unit *unit;892892+ int ret;893893+894894+ might_sleep();895895+896896+ if (!desc || desc->powered_on)897897+ return 0;898898+899899+ pwrseq = desc->pwrseq;900900+ target = desc->target;901901+ unit = target->unit;902902+903903+ guard(rwsem_read)(&pwrseq->rw_lock);904904+ if (!device_is_registered(&pwrseq->dev))905905+ return -ENODEV;906906+907907+ scoped_guard(mutex, &pwrseq->state_lock) {908908+ ret = pwrseq_unit_enable(pwrseq, unit);909909+ if (!ret)910910+ desc->powered_on = true;911911+ }912912+913913+ if (target->post_enable) {914914+ ret = target->post_enable(pwrseq);915915+ if (ret) {916916+ pwrseq_unit_disable(pwrseq, unit);917917+ desc->powered_on = false;918918+ }919919+ }920920+921921+ return ret;922922+}923923+EXPORT_SYMBOL_GPL(pwrseq_power_on);924924+925925+/**926926+ * pwrseq_power_off() - Issue a power-off request on behalf of the consumer927927+ * device.928928+ * @desc: Descriptor referencing the power sequencer.929929+ *930930+ * This undoes the effects of pwrseq_power_on(). It issues a power-off request931931+ * on behalf of the consumer and when the last remaining user does so, the932932+ * power-down sequence will be started. If one is in progress, the function933933+ * will block until it's complete and then return.934934+ *935935+ * Returns:936936+ * 0 on success, negative error number on failure.937937+ */938938+int pwrseq_power_off(struct pwrseq_desc *desc)939939+{940940+ struct pwrseq_device *pwrseq;941941+ struct pwrseq_unit *unit;942942+ int ret;943943+944944+ might_sleep();945945+946946+ if (!desc || !desc->powered_on)947947+ return 0;948948+949949+ pwrseq = desc->pwrseq;950950+ unit = desc->target->unit;951951+952952+ guard(rwsem_read)(&pwrseq->rw_lock);953953+ if (!device_is_registered(&pwrseq->dev))954954+ return -ENODEV;955955+956956+ guard(mutex)(&pwrseq->state_lock);957957+958958+ ret = pwrseq_unit_disable(pwrseq, unit);959959+ if (!ret)960960+ desc->powered_on = false;961961+962962+ return ret;963963+}964964+EXPORT_SYMBOL_GPL(pwrseq_power_off);965965+966966+#if IS_ENABLED(CONFIG_DEBUG_FS)967967+968968+struct pwrseq_debugfs_count_ctx {969969+ struct device *dev;970970+ loff_t index;971971+};972972+973973+static int pwrseq_debugfs_seq_count(struct device *dev, void *data)974974+{975975+ struct pwrseq_debugfs_count_ctx *ctx = data;976976+977977+ ctx->dev = dev;978978+979979+ return ctx->index-- ? 0 : 1;980980+}981981+982982+static void *pwrseq_debugfs_seq_start(struct seq_file *seq, loff_t *pos)983983+{984984+ struct pwrseq_debugfs_count_ctx ctx;985985+986986+ ctx.dev = NULL;987987+ ctx.index = *pos;988988+989989+ /*990990+ * We're holding the lock for the entire printout so no need to fiddle991991+ * with device reference count.992992+ */993993+ down_read(&pwrseq_sem);994994+995995+ bus_for_each_dev(&pwrseq_bus, NULL, &ctx, pwrseq_debugfs_seq_count);996996+ if (!ctx.index)997997+ return NULL;998998+999999+ return ctx.dev;10001000+}10011001+10021002+static void *pwrseq_debugfs_seq_next(struct seq_file *seq, void *data,10031003+ loff_t *pos)10041004+{10051005+ struct device *curr = data;10061006+10071007+ ++*pos;10081008+10091009+ struct device *next __free(put_device) =10101010+ bus_find_next_device(&pwrseq_bus, curr);10111011+ return next;10121012+}10131013+10141014+static void pwrseq_debugfs_seq_show_target(struct seq_file *seq,10151015+ struct pwrseq_target *target)10161016+{10171017+ seq_printf(seq, " target: [%s] (target unit: [%s])\n",10181018+ target->name, target->unit->name);10191019+}10201020+10211021+static void pwrseq_debugfs_seq_show_unit(struct seq_file *seq,10221022+ struct pwrseq_unit *unit)10231023+{10241024+ struct pwrseq_unit_dep *ref;10251025+10261026+ seq_printf(seq, " unit: [%s] - enable count: %u\n",10271027+ unit->name, unit->enable_count);10281028+10291029+ if (list_empty(&unit->deps))10301030+ return;10311031+10321032+ seq_puts(seq, " dependencies:\n");10331033+ list_for_each_entry(ref, &unit->deps, list)10341034+ seq_printf(seq, " [%s]\n", ref->unit->name);10351035+}10361036+10371037+static int pwrseq_debugfs_seq_show(struct seq_file *seq, void *data)10381038+{10391039+ struct device *dev = data;10401040+ struct pwrseq_device *pwrseq = to_pwrseq_device(dev);10411041+ struct pwrseq_target *target;10421042+ struct pwrseq_unit *unit;10431043+10441044+ seq_printf(seq, "%s:\n", dev_name(dev));10451045+10461046+ seq_puts(seq, " targets:\n");10471047+ list_for_each_entry(target, &pwrseq->targets, list)10481048+ pwrseq_debugfs_seq_show_target(seq, target);10491049+10501050+ seq_puts(seq, " units:\n");10511051+ list_for_each_entry(unit, &pwrseq->units, list)10521052+ pwrseq_debugfs_seq_show_unit(seq, unit);10531053+10541054+ return 0;10551055+}10561056+10571057+static void pwrseq_debugfs_seq_stop(struct seq_file *seq, void *data)10581058+{10591059+ up_read(&pwrseq_sem);10601060+}10611061+10621062+static const struct seq_operations pwrseq_debugfs_sops = {10631063+ .start = pwrseq_debugfs_seq_start,10641064+ .next = pwrseq_debugfs_seq_next,10651065+ .show = pwrseq_debugfs_seq_show,10661066+ .stop = pwrseq_debugfs_seq_stop,10671067+};10681068+DEFINE_SEQ_ATTRIBUTE(pwrseq_debugfs);10691069+10701070+static struct dentry *pwrseq_debugfs_dentry;10711071+10721072+#endif /* CONFIG_DEBUG_FS */10731073+10741074+static int __init pwrseq_init(void)10751075+{10761076+ int ret;10771077+10781078+ ret = bus_register(&pwrseq_bus);10791079+ if (ret) {10801080+ pr_err("Failed to register the power sequencer bus\n");10811081+ return ret;10821082+ }10831083+10841084+#if IS_ENABLED(CONFIG_DEBUG_FS)10851085+ pwrseq_debugfs_dentry = debugfs_create_file("pwrseq", 0444, NULL, NULL,10861086+ &pwrseq_debugfs_fops);10871087+#endif /* CONFIG_DEBUG_FS */10881088+10891089+ return 0;10901090+}10911091+subsys_initcall(pwrseq_init);10921092+10931093+static void __exit pwrseq_exit(void)10941094+{10951095+#if IS_ENABLED(CONFIG_DEBUG_FS)10961096+ debugfs_remove_recursive(pwrseq_debugfs_dentry);10971097+#endif /* CONFIG_DEBUG_FS */10981098+10991099+ bus_unregister(&pwrseq_bus);11001100+}11011101+module_exit(pwrseq_exit);11021102+11031103+MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");11041104+MODULE_DESCRIPTION("Power Sequencing subsystem core");11051105+MODULE_LICENSE("GPL");
+336
drivers/power/sequencing/pwrseq-qcom-wcn.c
···11+// SPDX-License-Identifier: GPL-2.0-only22+/*33+ * Copyright (C) 2024 Linaro Ltd.44+ */55+66+#include <linux/clk.h>77+#include <linux/delay.h>88+#include <linux/device.h>99+#include <linux/gpio/consumer.h>1010+#include <linux/jiffies.h>1111+#include <linux/mod_devicetable.h>1212+#include <linux/module.h>1313+#include <linux/of.h>1414+#include <linux/platform_device.h>1515+#include <linux/regulator/consumer.h>1616+#include <linux/pwrseq/provider.h>1717+#include <linux/string.h>1818+#include <linux/types.h>1919+2020+struct pwrseq_qcom_wcn_pdata {2121+ const char *const *vregs;2222+ size_t num_vregs;2323+ unsigned int pwup_delay_ms;2424+ unsigned int gpio_enable_delay_ms;2525+};2626+2727+struct pwrseq_qcom_wcn_ctx {2828+ struct pwrseq_device *pwrseq;2929+ struct device_node *of_node;3030+ const struct pwrseq_qcom_wcn_pdata *pdata;3131+ struct regulator_bulk_data *regs;3232+ struct gpio_desc *bt_gpio;3333+ struct gpio_desc *wlan_gpio;3434+ struct clk *clk;3535+ unsigned long last_gpio_enable_jf;3636+};3737+3838+static void pwrseq_qcom_wcn_ensure_gpio_delay(struct pwrseq_qcom_wcn_ctx *ctx)3939+{4040+ unsigned long diff_jiffies;4141+ unsigned int diff_msecs;4242+4343+ if (!ctx->pdata->gpio_enable_delay_ms)4444+ return;4545+4646+ diff_jiffies = jiffies - ctx->last_gpio_enable_jf;4747+ diff_msecs = jiffies_to_msecs(diff_jiffies);4848+4949+ if (diff_msecs < ctx->pdata->gpio_enable_delay_ms)5050+ msleep(ctx->pdata->gpio_enable_delay_ms - diff_msecs);5151+}5252+5353+static int pwrseq_qcom_wcn_vregs_enable(struct pwrseq_device *pwrseq)5454+{5555+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);5656+5757+ return regulator_bulk_enable(ctx->pdata->num_vregs, ctx->regs);5858+}5959+6060+static int pwrseq_qcom_wcn_vregs_disable(struct pwrseq_device *pwrseq)6161+{6262+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);6363+6464+ return regulator_bulk_disable(ctx->pdata->num_vregs, ctx->regs);6565+}6666+6767+static const struct pwrseq_unit_data pwrseq_qcom_wcn_vregs_unit_data = {6868+ .name = "regulators-enable",6969+ .enable = pwrseq_qcom_wcn_vregs_enable,7070+ .disable = pwrseq_qcom_wcn_vregs_disable,7171+};7272+7373+static int pwrseq_qcom_wcn_clk_enable(struct pwrseq_device *pwrseq)7474+{7575+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);7676+7777+ return clk_prepare_enable(ctx->clk);7878+}7979+8080+static int pwrseq_qcom_wcn_clk_disable(struct pwrseq_device *pwrseq)8181+{8282+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);8383+8484+ clk_disable_unprepare(ctx->clk);8585+8686+ return 0;8787+}8888+8989+static const struct pwrseq_unit_data pwrseq_qcom_wcn_clk_unit_data = {9090+ .name = "clock-enable",9191+ .enable = pwrseq_qcom_wcn_clk_enable,9292+ .disable = pwrseq_qcom_wcn_clk_disable,9393+};9494+9595+static const struct pwrseq_unit_data *pwrseq_qcom_wcn_unit_deps[] = {9696+ &pwrseq_qcom_wcn_vregs_unit_data,9797+ &pwrseq_qcom_wcn_clk_unit_data,9898+ NULL9999+};100100+101101+static int pwrseq_qcom_wcn_bt_enable(struct pwrseq_device *pwrseq)102102+{103103+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);104104+105105+ pwrseq_qcom_wcn_ensure_gpio_delay(ctx);106106+ gpiod_set_value_cansleep(ctx->bt_gpio, 1);107107+ ctx->last_gpio_enable_jf = jiffies;108108+109109+ return 0;110110+}111111+112112+static int pwrseq_qcom_wcn_bt_disable(struct pwrseq_device *pwrseq)113113+{114114+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);115115+116116+ gpiod_set_value_cansleep(ctx->bt_gpio, 0);117117+118118+ return 0;119119+}120120+121121+static const struct pwrseq_unit_data pwrseq_qcom_wcn_bt_unit_data = {122122+ .name = "bluetooth-enable",123123+ .deps = pwrseq_qcom_wcn_unit_deps,124124+ .enable = pwrseq_qcom_wcn_bt_enable,125125+ .disable = pwrseq_qcom_wcn_bt_disable,126126+};127127+128128+static int pwrseq_qcom_wcn_wlan_enable(struct pwrseq_device *pwrseq)129129+{130130+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);131131+132132+ pwrseq_qcom_wcn_ensure_gpio_delay(ctx);133133+ gpiod_set_value_cansleep(ctx->wlan_gpio, 1);134134+ ctx->last_gpio_enable_jf = jiffies;135135+136136+ return 0;137137+}138138+139139+static int pwrseq_qcom_wcn_wlan_disable(struct pwrseq_device *pwrseq)140140+{141141+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);142142+143143+ gpiod_set_value_cansleep(ctx->wlan_gpio, 0);144144+145145+ return 0;146146+}147147+148148+static const struct pwrseq_unit_data pwrseq_qcom_wcn_wlan_unit_data = {149149+ .name = "wlan-enable",150150+ .deps = pwrseq_qcom_wcn_unit_deps,151151+ .enable = pwrseq_qcom_wcn_wlan_enable,152152+ .disable = pwrseq_qcom_wcn_wlan_disable,153153+};154154+155155+static int pwrseq_qcom_wcn_pwup_delay(struct pwrseq_device *pwrseq)156156+{157157+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);158158+159159+ if (ctx->pdata->pwup_delay_ms)160160+ msleep(ctx->pdata->pwup_delay_ms);161161+162162+ return 0;163163+}164164+165165+static const struct pwrseq_target_data pwrseq_qcom_wcn_bt_target_data = {166166+ .name = "bluetooth",167167+ .unit = &pwrseq_qcom_wcn_bt_unit_data,168168+ .post_enable = pwrseq_qcom_wcn_pwup_delay,169169+};170170+171171+static const struct pwrseq_target_data pwrseq_qcom_wcn_wlan_target_data = {172172+ .name = "wlan",173173+ .unit = &pwrseq_qcom_wcn_wlan_unit_data,174174+ .post_enable = pwrseq_qcom_wcn_pwup_delay,175175+};176176+177177+static const struct pwrseq_target_data *pwrseq_qcom_wcn_targets[] = {178178+ &pwrseq_qcom_wcn_bt_target_data,179179+ &pwrseq_qcom_wcn_wlan_target_data,180180+ NULL181181+};182182+183183+static const char *const pwrseq_qca6390_vregs[] = {184184+ "vddio",185185+ "vddaon",186186+ "vddpmu",187187+ "vddrfa0p95",188188+ "vddrfa1p3",189189+ "vddrfa1p9",190190+ "vddpcie1p3",191191+ "vddpcie1p9",192192+};193193+194194+static const struct pwrseq_qcom_wcn_pdata pwrseq_qca6390_of_data = {195195+ .vregs = pwrseq_qca6390_vregs,196196+ .num_vregs = ARRAY_SIZE(pwrseq_qca6390_vregs),197197+ .pwup_delay_ms = 60,198198+ .gpio_enable_delay_ms = 100,199199+};200200+201201+static const char *const pwrseq_wcn7850_vregs[] = {202202+ "vdd",203203+ "vddio",204204+ "vddio1p2",205205+ "vddaon",206206+ "vdddig",207207+ "vddrfa1p2",208208+ "vddrfa1p8",209209+};210210+211211+static const struct pwrseq_qcom_wcn_pdata pwrseq_wcn7850_of_data = {212212+ .vregs = pwrseq_wcn7850_vregs,213213+ .num_vregs = ARRAY_SIZE(pwrseq_wcn7850_vregs),214214+ .pwup_delay_ms = 50,215215+};216216+217217+static int pwrseq_qcom_wcn_match(struct pwrseq_device *pwrseq,218218+ struct device *dev)219219+{220220+ struct pwrseq_qcom_wcn_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);221221+ struct device_node *dev_node = dev->of_node;222222+223223+ /*224224+ * The PMU supplies power to the Bluetooth and WLAN modules. both225225+ * consume the PMU AON output so check the presence of the226226+ * 'vddaon-supply' property and whether it leads us to the right227227+ * device.228228+ */229229+ if (!of_property_present(dev_node, "vddaon-supply"))230230+ return 0;231231+232232+ struct device_node *reg_node __free(device_node) =233233+ of_parse_phandle(dev_node, "vddaon-supply", 0);234234+ if (!reg_node)235235+ return 0;236236+237237+ /*238238+ * `reg_node` is the PMU AON regulator, its parent is the `regulators`239239+ * node and finally its grandparent is the PMU device node that we're240240+ * looking for.241241+ */242242+ if (!reg_node->parent || !reg_node->parent->parent ||243243+ reg_node->parent->parent != ctx->of_node)244244+ return 0;245245+246246+ return 1;247247+}248248+249249+static int pwrseq_qcom_wcn_probe(struct platform_device *pdev)250250+{251251+ struct device *dev = &pdev->dev;252252+ struct pwrseq_qcom_wcn_ctx *ctx;253253+ struct pwrseq_config config;254254+ int i, ret;255255+256256+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);257257+ if (!ctx)258258+ return -ENOMEM;259259+260260+ ctx->of_node = dev->of_node;261261+262262+ ctx->pdata = of_device_get_match_data(dev);263263+ if (!ctx->pdata)264264+ return dev_err_probe(dev, -ENODEV,265265+ "Failed to obtain platform data\n");266266+267267+ ctx->regs = devm_kcalloc(dev, ctx->pdata->num_vregs,268268+ sizeof(*ctx->regs), GFP_KERNEL);269269+ if (!ctx->regs)270270+ return -ENOMEM;271271+272272+ for (i = 0; i < ctx->pdata->num_vregs; i++)273273+ ctx->regs[i].supply = ctx->pdata->vregs[i];274274+275275+ ret = devm_regulator_bulk_get(dev, ctx->pdata->num_vregs, ctx->regs);276276+ if (ret < 0)277277+ return dev_err_probe(dev, ret,278278+ "Failed to get all regulators\n");279279+280280+ ctx->bt_gpio = devm_gpiod_get_optional(dev, "bt-enable", GPIOD_OUT_LOW);281281+ if (IS_ERR(ctx->bt_gpio))282282+ return dev_err_probe(dev, PTR_ERR(ctx->bt_gpio),283283+ "Failed to get the Bluetooth enable GPIO\n");284284+285285+ ctx->wlan_gpio = devm_gpiod_get_optional(dev, "wlan-enable",286286+ GPIOD_OUT_LOW);287287+ if (IS_ERR(ctx->wlan_gpio))288288+ return dev_err_probe(dev, PTR_ERR(ctx->wlan_gpio),289289+ "Failed to get the WLAN enable GPIO\n");290290+291291+ ctx->clk = devm_clk_get_optional(dev, NULL);292292+ if (IS_ERR(ctx->clk))293293+ return dev_err_probe(dev, PTR_ERR(ctx->clk),294294+ "Failed to get the reference clock\n");295295+296296+ memset(&config, 0, sizeof(config));297297+298298+ config.parent = dev;299299+ config.owner = THIS_MODULE;300300+ config.drvdata = ctx;301301+ config.match = pwrseq_qcom_wcn_match;302302+ config.targets = pwrseq_qcom_wcn_targets;303303+304304+ ctx->pwrseq = devm_pwrseq_device_register(dev, &config);305305+ if (IS_ERR(ctx->pwrseq))306306+ return dev_err_probe(dev, PTR_ERR(ctx->pwrseq),307307+ "Failed to register the power sequencer\n");308308+309309+ return 0;310310+}311311+312312+static const struct of_device_id pwrseq_qcom_wcn_of_match[] = {313313+ {314314+ .compatible = "qcom,qca6390-pmu",315315+ .data = &pwrseq_qca6390_of_data,316316+ },317317+ {318318+ .compatible = "qcom,wcn7850-pmu",319319+ .data = &pwrseq_wcn7850_of_data,320320+ },321321+ { }322322+};323323+MODULE_DEVICE_TABLE(of, pwrseq_qcom_wcn_of_match);324324+325325+static struct platform_driver pwrseq_qcom_wcn_driver = {326326+ .driver = {327327+ .name = "pwrseq-qcom_wcn",328328+ .of_match_table = pwrseq_qcom_wcn_of_match,329329+ },330330+ .probe = pwrseq_qcom_wcn_probe,331331+};332332+module_platform_driver(pwrseq_qcom_wcn_driver);333333+334334+MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");335335+MODULE_DESCRIPTION("Qualcomm WCN PMU power sequencing driver");336336+MODULE_LICENSE("GPL");
···11+/* SPDX-License-Identifier: GPL-2.0-only */22+/*33+ * Copyright (C) 2024 Linaro Ltd.44+ */55+66+#ifndef __POWER_SEQUENCING_PROVIDER_H__77+#define __POWER_SEQUENCING_PROVIDER_H__88+99+struct device;1010+struct module;1111+struct pwrseq_device;1212+1313+typedef int (*pwrseq_power_state_func)(struct pwrseq_device *);1414+typedef int (*pwrseq_match_func)(struct pwrseq_device *, struct device *);1515+1616+/**1717+ * struct pwrseq_unit_data - Configuration of a single power sequencing1818+ * unit.1919+ * @name: Name of the unit.2020+ * @deps: Units that must be enabled before this one and disabled after it2121+ * in the order they come in this array. Must be NULL-terminated.2222+ * @enable: Callback running the part of the power-on sequence provided by2323+ * this unit.2424+ * @disable: Callback running the part of the power-off sequence provided2525+ * by this unit.2626+ */2727+struct pwrseq_unit_data {2828+ const char *name;2929+ const struct pwrseq_unit_data **deps;3030+ pwrseq_power_state_func enable;3131+ pwrseq_power_state_func disable;3232+};3333+3434+/**3535+ * struct pwrseq_target_data - Configuration of a power sequencing target.3636+ * @name: Name of the target.3737+ * @unit: Final unit that this target must reach in order to be considered3838+ * enabled.3939+ * @post_enable: Callback run after the target unit has been enabled, *after*4040+ * the state lock has been released. It's useful for implementing4141+ * boot-up delays without blocking other users from powering up4242+ * using the same power sequencer.4343+ */4444+struct pwrseq_target_data {4545+ const char *name;4646+ const struct pwrseq_unit_data *unit;4747+ pwrseq_power_state_func post_enable;4848+};4949+5050+/**5151+ * struct pwrseq_config - Configuration used for registering a new provider.5252+ * @parent: Parent device for the sequencer. Must be set.5353+ * @owner: Module providing this device.5454+ * @drvdata: Private driver data.5555+ * @match: Provider callback used to match the consumer device to the sequencer.5656+ * @targets: Array of targets for this power sequencer. Must be NULL-terminated.5757+ */5858+struct pwrseq_config {5959+ struct device *parent;6060+ struct module *owner;6161+ void *drvdata;6262+ pwrseq_match_func match;6363+ const struct pwrseq_target_data **targets;6464+};6565+6666+struct pwrseq_device *6767+pwrseq_device_register(const struct pwrseq_config *config);6868+void pwrseq_device_unregister(struct pwrseq_device *pwrseq);6969+struct pwrseq_device *7070+devm_pwrseq_device_register(struct device *dev,7171+ const struct pwrseq_config *config);7272+7373+void *pwrseq_device_get_drvdata(struct pwrseq_device *pwrseq);7474+7575+#endif /* __POWER_SEQUENCING_PROVIDER_H__ */