linux中的regulator

2019-07-14 00:17发布

Regulator模块用于控制系统中某些设备的电压/电流供应。在嵌入式系统(尤其是手机)中,控制耗电量很重要,直接影响到电池的续航时间。所以,如果系统中某一个模块暂时不需要使用,就可以通过regulator关闭其电源供应;或者降低提供给该模块的电压、电流大小。

regulator常用概念

Regulator : 电源芯片, 比如电压转换芯片
Consumer : 消费者,使用电源的部件, Regulator是给Consumer供电的
machine : 单板,上面焊接有Regulator和Consumer
Constraints : 约束, 比如某个电源管理芯片输出的电压范围
Supply : 提供电源的部件, Regulator就是一个Supply; Regulator A可以给Regulator B供电, 那么Regulator B的Supply就是A regulaor :常用的为电压调节器,总共有以下几类
ldo :输入/输出电压差别不大时使用,电路简单,转换效率较低,如果输入/输出电压相差较大损耗就高,造成芯片发烫
dcdc:输入/输出压差别较大时使用,电路比较复杂,转换效率高
pmu : 集成了多个ldo、dcdc,可以工资每个单元电压的输出大小

regulator模块架构

主要作用: 给外设(consumer)供电
framwork层次:主要分为 machine、regulator、consumer三部分 machine
(1) 指定了regulaor和consumer的对应关系
(consumer dev的name和给consumer 引脚供电的引脚名字)
(2) 约束调节:regulator的电压范围等等 驱动要做的事情:
注册一个platform_device: 在它的私有数据里指定regulator和consume的对应关系(这个电源芯片给哪一个部件供电)
指定约束条件(比如电压范围) regulator
电源芯片本身的驱动实现的fops
.enable
.disable
.set_votage
.set_current regulator_register后在 regulator_list里就有了consumer dev的name和给consumer 引脚供电的引脚名字 驱动要做的事情:
注册一个platform_driver: 在它的probe函数里分配、设置、注册一个regulator
“设置”里要做的事情: 实现regulator的操作, 比如enable, disable, set_voltage consumer
使用即可
regulator_get, regulator_enable, regulator_disable, regulator_set_voltage regulator_get(struct device *dev, const char *id) // dev中包含了name,id指定了这个regulator给consumer那个电源引脚供电

regulator重要的结构体

regulator_dev代表一个regulator设备 /* * struct regulator_dev * * Voltage / Current regulator class device. One for each * regulator. * * This should *not* be used directly by anything except the regulator * core and notification injection (which should take the mutex and do * no other direct access). */ struct regulator_dev { const struct regulator_desc *desc;//描述符,包括regulator的名称、ID、regulator_ops等 int exclusive; u32 use_count; // 使用计数 u32 open_count; u32 bypass_count; /* lists we belong to */ struct list_head list; /* list of all regulators */ /* lists we own */ struct list_head consumer_list; /* consumers we supply */// 此regulator负责供电的设备列表 struct blocking_notifier_head notifier; struct mutex mutex; /* consumer lock */ struct module *owner; struct device dev; struct regulation_constraints *constraints; struct regulator *supply; /* for tree */ const char *supply_name; struct regmap *regmap; struct delayed_work disable_work; int deferred_disables; void *reg_data; /* regulator_dev data */ struct dentry *debugfs; struct regulator_enable_gpio *ena_pin; unsigned int ena_gpio_state:1; /* time when this regulator was disabled last time */ unsigned long last_off_jiffy; }; regulator_init_data在初始化时使用,用来建立父子regulator、受电模块之间的树状结构,以及一些regulator的基本参数。 /** * struct regulator_init_data - regulator platform initialisation data. * * Initialisation constraints, our supply and consumers supplies. * * @supply_regulator: Parent regulator. Specified using the regulator name * as it appears in the name field in sysfs, which can * be explicitly set using the constraints field 'name'. * * @constraints: Constraints. These must be specified for the regulator to * be usable. * @num_consumer_supplies: Number of consumer device supplies. * @consumer_supplies: Consumer device supply configuration. * * @regulator_init: Callback invoked when the regulator has been registered. * @driver_data: Data passed to regulator_init. */ struct regulator_init_data { const char *supply_regulator; /* or NULL for system supply */ struct regulation_constraints constraints; int num_consumer_supplies; struct regulator_consumer_supply *consumer_supplies; /* optional regulator machine specific init */ int (*regulator_init)(void *driver_data); void *driver_data; /* core does not touch this */ }; regulator_desc 结构体 /** * struct regulator_desc - Static regulator descriptor * * Each regulator registered with the core is described with a * structure of this type and a struct regulator_config. This * structure contains the non-varying parts of the regulator * description. * * @name: Identifying name for the regulator. * @supply_name: Identifying the regulator supply * @of_match: Name used to identify regulator in DT. * @regulators_node: Name of node containing regulator definitions in DT. * @of_parse_cb: Optional callback called only if of_match is present. * Will be called for each regulator parsed from DT, during * init_data parsing. * The regulator_config passed as argument to the callback will * be a copy of config passed to regulator_register, valid only * for this particular call. Callback may freely change the * config but it cannot store it for later usage. * Callback should return 0 on success or negative ERRNO * indicating failure. * @id: Numerical identifier for the regulator. * @ops: Regulator operations table. * @irq: Interrupt number for the regulator. * @type: Indicates if the regulator is a voltage or current regulator. * @owner: Module providing the regulator, used for refcounting. * * @continuous_voltage_range: Indicates if the regulator can set any * voltage within constrains range. * @n_voltages: Number of selectors available for ops.list_voltage(). * * @min_uV: Voltage given by the lowest selector (if linear mapping) * @uV_step: Voltage increase with each selector (if linear mapping) * @linear_min_sel: Minimal selector for starting linear mapping * @fixed_uV: Fixed voltage of rails. * @ramp_delay: Time to settle down after voltage change (unit: uV/us) * @min_dropout_uV: The minimum dropout voltage this regulator can handle * @linear_ranges: A constant table of possible voltage ranges. * @n_linear_ranges: Number of entries in the @linear_ranges table. * @volt_table: Voltage mapping table (if table based mapping) * * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ * @vsel_mask: Mask for register bitfield used for selector * @csel_reg: Register for TPS65218 LS3 current regulator * @csel_mask: Mask for TPS65218 LS3 current regulator * @apply_reg: Register for initiate voltage change on the output when * using regulator_set_voltage_sel_regmap * @apply_bit: Register bitfield used for initiate voltage change on the * output when using regulator_set_voltage_sel_regmap * @enable_reg: Register for control when using regmap enable/disable ops * @enable_mask: Mask for control when using regmap enable/disable ops * @enable_val: Enabling value for control when using regmap enable/disable ops * @disable_val: Disabling value for control when using regmap enable/disable ops * @enable_is_inverted: A flag to indicate set enable_mask bits to disable * when using regulator_enable_regmap and friends APIs. * @bypass_reg: Register for control when using regmap set_bypass * @bypass_mask: Mask for control when using regmap set_bypass * @bypass_val_on: Enabling value for control when using regmap set_bypass * @bypass_val_off: Disabling value for control when using regmap set_bypass * @active_discharge_off: Enabling value for control when using regmap * set_active_discharge * @active_discharge_on: Disabling value for control when using regmap * set_active_discharge * @active_discharge_mask: Mask for control when using regmap * set_active_discharge * @active_discharge_reg: Register for control when using regmap * set_active_discharge * * @enable_time: Time taken for initial enable of regulator (in uS). * @off_on_delay: guard time (in uS), before re-enabling a regulator * * @of_map_mode: Maps a hardware mode defined in a DeviceTree to a standard mode */ struct regulator_desc { const char *name; const char *supply_name; const char *of_match; const char *regulators_node; int (*of_parse_cb)(struct device_node *, const struct regulator_desc *, struct regulator_config *); int id; unsigned int continuous_voltage_range:1; unsigned n_voltages; const struct regulator_ops *ops; int irq; enum regulator_type type; struct module *owner; unsigned int min_uV; unsigned int uV_step; unsigned int linear_min_sel; int fixed_uV; unsigned int ramp_delay; int min_dropout_uV; const struct regulator_linear_range *linear_ranges; int n_linear_ranges; const unsigned int *volt_table; unsigned int vsel_reg; unsigned int vsel_mask; unsigned int csel_reg; unsigned int csel_mask; unsigned int apply_reg; unsigned int apply_bit; unsigned int enable_reg; unsigned int enable_mask; unsigned int enable_val; unsigned int disable_val; bool enable_is_inverted; unsigned int bypass_reg; unsigned int bypass_mask; unsigned int bypass_val_on; unsigned int bypass_val_off; unsigned int active_discharge_on; unsigned int active_discharge_off; unsigned int active_discharge_mask; unsigned int active_discharge_reg; unsigned int enable_time; unsigned int off_on_delay; unsigned int (*of_map_mode)(unsigned int mode); }; 其它结构体
struct regulator设备驱动直接操作的结构体
struct regulation_constraints regulator限制范围,用于初始化
struct regulator_consumer_supply regulator的consumer信息
struct regulator_map 为consumers与regulator对应表
struct regulator_config regulator的动态运行信息
struct regulator_opsregulator的操作函数集合

注册regulator

通过regulator_register函数登记生成一个regulator_dev /** * regulator_register - register regulator * @regulator_desc: regulator to register * @cfg: runtime configuration for regulator * * Called by regulator drivers to register a regulator. * Returns a valid pointer to struct regulator_dev on success * or an ERR_PTR() on error. */ struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc, const struct regulator_config *cfg) { const struct regulation_constraints *constraints = NULL; const struct regulator_init_data *init_data; struct regulator_config *config = NULL; static atomic_t regulator_no = ATOMIC_INIT(-1); struct regulator_dev *rdev; struct device *dev; int ret, i; if (regulator_desc == NULL || cfg == NULL) return ERR_PTR(-EINVAL); dev = cfg->dev; WARN_ON(!dev); if (regulator_desc->name == NULL || regulator_desc->ops == NULL) return ERR_PTR(-EINVAL); if (regulator_desc->type != REGULATOR_VOLTAGE && regulator_desc->type != REGULATOR_CURRENT) return ERR_PTR(-EINVAL); /* Only one of each should be implemented */ WARN_ON(regulator_desc->ops->get_voltage && regulator_desc->ops->get_voltage_sel); WARN_ON(regulator_desc->ops->set_voltage && regulator_desc->ops->set_voltage_sel); /* If we're using selectors we must implement list_voltage. */ if (regulator_desc->ops->get_voltage_sel && !regulator_desc->ops->list_voltage) { return ERR_PTR(-EINVAL); } if (regulator_desc->ops->set_voltage_sel && !regulator_desc->ops->list_voltage) { return ERR_PTR(-EINVAL); } rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); if (rdev == NULL) return ERR_PTR(-ENOMEM); /* * Duplicate the config so the driver could override it after * parsing init data. */ config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL); if (config == NULL) { kfree(rdev); return ERR_PTR(-ENOMEM); } init_data = regulator_of_get_init_data(dev, regulator_desc, config, &rdev->dev.of_node); if (!init_data) { init_data = config->init_data; rdev->dev.of_node = of_node_get(config->of_node); } mutex_init(&rdev->mutex); rdev->reg_data = config->driver_data; rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; if (config->regmap) rdev->regmap = config->regmap; else if (dev_get_regmap(dev, NULL)) rdev->regmap = dev_get_regmap(dev, NULL); else if (dev->parent) rdev->regmap = dev_get_regmap(dev->parent, NULL); INIT_LIST_HEAD(&rdev->consumer_list); INIT_LIST_HEAD(&rdev->list); BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work); /* preform any regulator specific init */ if (init_data && init_data->regulator_init) { ret = init_data->regulator_init(rdev->reg_data); if (ret < 0) goto clean; } if ((config->ena_gpio || config->ena_gpio_initialized) && gpio_is_valid(config->ena_gpio)) { mutex_lock(&regulator_list_mutex); ret = regulator_ena_gpio_request(rdev, config); mutex_unlock(&regulator_list_mutex); if (ret != 0) { rdev_err(rdev, "Failed to request enable GPIO%d: %d ", config->ena_gpio, ret); goto clean; } } /* register with sysfs */ rdev->dev.class = &regulator_class; rdev->dev.parent = dev; dev_set_name(&rdev->dev, "regulator.%lu", (unsigned long) atomic_inc_return(&regulator_no)); /* set regulator constraints */ if (init_data) constraints = &init_data->constraints; if (init_data && init_data->supply_regulator) rdev->supply_name = init_data->supply_regulator; else if (regulator_desc->supply_name) rdev->supply_name = regulator_desc->supply_name; /* * Attempt to resolve the regulator supply, if specified, * but don't return an error if we fail because we will try * to resolve it again later as more regulators are added. */ if (regulator_resolve_supply(rdev)) rdev_dbg(rdev, "unable to resolve supply "); ret = set_machine_constraints(rdev, constraints); if (ret < 0) goto wash; /* add consumers devices */ if (init_data) { mutex_lock(&regulator_list_mutex); for (i = 0; i < init_data->num_consumer_supplies; i++) { ret = set_consumer_device_supply(rdev, init_data->consumer_supplies[i].dev_name, init_data->consumer_supplies[i].supply); if (ret < 0) { mutex_unlock(®ulator_list_mutex); dev_err(dev, "Failed to set supply %s ", init_data->consumer_supplies[i].supply); goto unset_supplies; } } mutex_unlock(®ulator_list_mutex); } ret = device_register(&rdev->dev); if (ret != 0) { put_device(&rdev->dev); goto unset_supplies; } dev_set_drvdata(&rdev->dev, rdev); rdev_init_debugfs(rdev); /* try to resolve regulators supply since a new one was registered */ class_for_each_device(®ulator_class, NULL, NULL, regulator_register_resolve_supply); kfree(config); return rdev; unset_supplies: mutex_lock(®ulator_list_mutex); unset_regulator_supplies(rdev); mutex_unlock(®ulator_list_mutex); wash: kfree(rdev->constraints); mutex_lock(®ulator_list_mutex); regulator_ena_gpio_free(rdev); mutex_unlock(®ulator_list_mutex); clean: kfree(rdev); kfree(config); return ERR_PTR(ret); } set_consumer_device_supply函数用于登记regulator_devcomsumer_dev(regulator负责供电的设备)之间的对应关系。对于每一个regulator_devcomsumer_dev的配对,都会有一个regulator_map结构,这些结构会被加入到全局链表regulator_map_list中。 在设备驱动使用regulator对其驱动的设备供电时,需要首先保证设备与对应regulator之间的匹配关系已经被登记到regulator框架中。
设备驱动通过regulator_get函数得到regulator结构,此函数通过regulator_map_list找到对应regulator_dev,再生成regulator结构给用户使用。 通过regulator_enable/regulator_disable打开、关闭regulator,这两个函数最终都是调用struct regulator_ops里的对应成员。
除此之外,还有regualtor_set_voltage / regulator_get_voltage等等。