max20087 regulator 注册与regulator框架

1. max20087 regulator注册

目前在调试美信的max9286_max96705,转接板上需要调试gpio扩展芯片tca6408和电源保护芯片max20087(会生成4路regulator)

max20087 driver

	/*
	 * max20087-regulator.c - Regulator device (Power Protector)
	 *                        driver for MAX20087
	 *
	 * Copyright (C) 2018  Avnet, Inc.
	 *
	 * This library is free software; you can redistribute it and/or
	 * modify it under the terms of the GNU Library General Public
	 * License as published by the Free Software Foundation; either
	 * version 2 of the License, or (at your option) any later version.
	 *
	 * This library is distributed in the hope that it will be useful,
	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	 * Library General Public License for more details.
	 *
	 * This driver is based on da9211-regulator.c
	 */
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regmap.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/regulator/of_regulator.h>

/* Register Offset */
#define MAX20087_REG_MASK		0x00
#define MAX20087_REG_CONFIG		0x01
#define	MAX20087_REG_ID			0x02
#define	MAX20087_REG_STAT1		0x03
#define	MAX20087_REG_STAT2_L	0x04
#define	MAX20087_REG_STAT2_H	0x05
#define	MAX20087_REG_ADC1		0x06
#define	MAX20087_REG_ADC2		0x07
#define	MAX20087_REG_ADC3		0x08
#define	MAX20087_REG_ADC4		0x09

/* DEVICE IDs */
#define MAX20087_DEVICE_ID	0x20
#define DEVICE_ID_MASK		0xf0

/* MAX20087 REGULATOR IDs */
#define MAX20087_ID_OUT1	0
#define MAX20087_ID_OUT2	1
#define MAX20087_ID_OUT3	2
#define MAX20087_ID_OUT4	3

/* Register bits */
#define MAX20087_EN_MASK			0x0f
#define MAX20087_EN_ALL				0x0f
#define MAX20087_EN_OUT1			0x01
#define MAX20087_EN_OUT2			0x02
#define MAX20087_EN_OUT3			0x04
#define MAX20087_EN_OUT4			0x08
#define MAX20087_INT_DISABLE_ALL	0x3f

/* Default limits measured in millivolts and milliamps */
#define MAX20087_MIN_MV		1200	// dummy
#define MAX20087_MAX_MV		1200	// dummy
#define MAX20087_STEP_MV	10		// dummy

#define MAX20087_MAX_REGULATORS	4

enum max20087_chip_id {
	MAX20087_ID,
};

struct max20087 {
	struct device *dev;
	struct regmap *regmap;
	int num_output;
	struct regulator_dev *rdev[MAX20087_MAX_REGULATORS];
	struct device_node *reg_node[MAX20087_MAX_REGULATORS];
	struct regulator_init_data *init_data[MAX20087_MAX_REGULATORS];
	struct gpio_desc *gpio_en;
	int chip_irq; // reserved
	int chip_id;
};

static unsigned int max20087_get_mode(struct regulator_dev *rdev)
{
	return 0; // Dummy
}

static int max20087_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
	return 0; // Dummy
}

static int max20087_set_current_limit(struct regulator_dev *rdev, int min,
				      int max)
{
	return 0; // Dummy
}

static int max20087_get_current_limit(struct regulator_dev *rdev)
{
	return 0; // Dummy
}

static const struct regulator_ops max20087_buck_ops = {
	.get_mode = max20087_get_mode,
	.set_mode = max20087_set_mode,
	.enable = regulator_enable_regmap,
	.disable = regulator_disable_regmap,
	.is_enabled = regulator_is_enabled_regmap,
	.list_voltage = regulator_list_voltage_linear,
	.set_current_limit = max20087_set_current_limit,
	.get_current_limit = max20087_get_current_limit,
};

#define MAX20087_OUT1 \
{\
	.name = "OUT1",\
	.ops = &max20087_buck_ops,\
	.type = REGULATOR_VOLTAGE,\
	.id = MAX20087_ID_OUT1,\
	.n_voltages = \
		(MAX20087_MAX_MV - MAX20087_MIN_MV) / MAX20087_STEP_MV + 1,\
	.min_uV = (MAX20087_MIN_MV * 1000),\
	.uV_step = (MAX20087_STEP_MV * 1000),\
	.enable_reg = MAX20087_REG_CONFIG,\
	.enable_mask = MAX20087_EN_OUT1,\
	.enable_val = MAX20087_EN_OUT1,\
	.disable_val = 0,\
	.owner = THIS_MODULE,\
}

#define MAX20087_OUT2 \
{\
	.name = "OUT2",\
	.ops = &max20087_buck_ops,\
	.type = REGULATOR_VOLTAGE,\
	.id = MAX20087_ID_OUT2,\
	.n_voltages = \
		(MAX20087_MAX_MV - MAX20087_MIN_MV) / MAX20087_STEP_MV + 1,\
	.min_uV = (MAX20087_MIN_MV * 1000),\
	.uV_step = (MAX20087_STEP_MV * 1000),\
	.enable_reg = MAX20087_REG_CONFIG,\
	.enable_mask = MAX20087_EN_OUT2,\
	.enable_val = MAX20087_EN_OUT2,\
	.disable_val = 0,\
	.owner = THIS_MODULE,\
}

#define MAX20087_OUT3 \
{\
	.name = "OUT3",\
	.ops = &max20087_buck_ops,\
	.type = REGULATOR_VOLTAGE,\
	.id = MAX20087_ID_OUT3,\
	.n_voltages = \
		(MAX20087_MAX_MV - MAX20087_MIN_MV) / MAX20087_STEP_MV + 1,\
	.min_uV = (MAX20087_MIN_MV * 1000),\
	.uV_step = (MAX20087_STEP_MV * 1000),\
	.enable_reg = MAX20087_REG_CONFIG,\
	.enable_mask = MAX20087_EN_OUT3,\
	.enable_val = MAX20087_EN_OUT3,\
	.disable_val = 0,\
	.owner = THIS_MODULE,\
}

#define MAX20087_OUT4 \
{\
	.name = "OUT4",\
	.ops = &max20087_buck_ops,\
	.type = REGULATOR_VOLTAGE,\
	.id = MAX20087_ID_OUT4,\
	.n_voltages = \
		(MAX20087_MAX_MV - MAX20087_MIN_MV) / MAX20087_STEP_MV + 1,\
	.min_uV = (MAX20087_MIN_MV * 1000),\
	.uV_step = (MAX20087_STEP_MV * 1000),\
	.enable_reg = MAX20087_REG_CONFIG,\
	.enable_mask = MAX20087_EN_OUT4,\
	.enable_val = MAX20087_EN_OUT4,\
	.disable_val = 0,\
	.owner = THIS_MODULE,\
}

static struct regulator_desc max20087_regulators[] = {
	MAX20087_OUT1,
	MAX20087_OUT2,
	MAX20087_OUT3,
	MAX20087_OUT4,
};

static struct of_regulator_match max20087_matches[] = {
	[MAX20087_ID_OUT1] = { .name = "OUT1" },
	[MAX20087_ID_OUT2] = { .name = "OUT2" },
	[MAX20087_ID_OUT3] = { .name = "OUT3" },
	[MAX20087_ID_OUT4] = { .name = "OUT4" },
	};

static int max20087_parse_regulators_dt(struct max20087 *chip)
{
	struct device_node *node;
	int i, num, n;

	/* Get the mux out of reset if a reset GPIO is specified. */
	chip->gpio_en = devm_gpiod_get(chip->dev, "enable", GPIOD_OUT_HIGH);
	if (IS_ERR(chip->gpio_en))
		return PTR_ERR(chip->gpio_en);

	node = of_get_child_by_name(chip->dev->of_node, "regulators");
	if (!node) {
		dev_err(chip->dev, "regulators node not found\n");
		//return ERR_PTR(-ENODEV);
		return PTR_ERR(node);
	}

	num = of_regulator_match(chip->dev, node,
				 max20087_matches,
				 ARRAY_SIZE(max20087_matches));
	of_node_put(node);
	if (num < 0) {
		dev_err(chip->dev, "Failed to match regulators\n");
		//return ERR_PTR(-EINVAL);
		return -EINVAL;
	}

	chip->num_output = num;

	n = 0;
	for (i = 0; i < ARRAY_SIZE(max20087_matches); i++) {
		if (!max20087_matches[i].init_data)
			continue;

		chip->init_data[n] = max20087_matches[i].init_data;
		chip->reg_node[n] = max20087_matches[i].of_node;
		dev_dbg(chip->dev, "name %s\n", max20087_matches[i].name);
		printk("%s: name %s\n", __func__, max20087_matches[i].name); // testing

		n++;
	}

	return 0;
}

static irqreturn_t max20087_irq_handler(int irq, void *data)
{
	// Not support interrupt yet
	// TODO: Implement interrupt handler code
	return IRQ_NONE;
}

static int max20087_regulator_init(struct max20087 *chip)
{
	struct regulator_config config = { };
	int i, ret;

	for (i = 0; i < chip->num_output; i++) {
		config.init_data = chip->init_data[i];
		config.dev = chip->dev;
		config.driver_data = chip;
		config.regmap = chip->regmap;
		config.of_node = chip->reg_node[i];
		config.ena_gpiod = chip->gpio_en;
		//config.ena_gpio_initialized = false;

		chip->rdev[i] = devm_regulator_register(chip->dev,
					&max20087_regulators[i], &config);
		if (IS_ERR(chip->rdev[i])) {
			dev_err(chip->dev,
				"Failed to register MAX20087 regulator\n");
			return PTR_ERR(chip->rdev[i]);
		}
	}

	// TODO: Enable interrupt support
	ret = regmap_update_bits(chip->regmap, MAX20087_REG_MASK,
				 MAX20087_INT_DISABLE_ALL,
				 MAX20087_INT_DISABLE_ALL);
	if (ret < 0) {
		dev_err(chip->dev,
			"Failed to update mask reg: %d\n", ret);
		return ret;
	}

	return 0;
}

static bool max20087_gen_is_writeable_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case MAX20087_REG_MASK:
	case MAX20087_REG_CONFIG:
		return true;
	default:
		return false;
	}
}

static const struct regmap_config max20087_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,
	.writeable_reg = max20087_gen_is_writeable_reg,
	.max_register = 0x9,
	.cache_type = REGCACHE_NONE,
};

/*
 * I2C driver interface functions
 */
static int max20087_i2c_probe(struct i2c_client *i2c,
			      const struct i2c_device_id *id)
{
	struct max20087 *chip;
	int error, ret;
	unsigned int data;
	printk("%s: enter\n", __func__); // testing

	chip = devm_kzalloc(&i2c->dev, sizeof(struct max20087), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	chip->dev = &i2c->dev;
	chip->regmap = devm_regmap_init_i2c(i2c, &max20087_regmap_config);
	if (IS_ERR(chip->regmap)) {
		error = PTR_ERR(chip->regmap);
		dev_err(chip->dev, "Failed to allocate register map: %d\n",
			error);
		return error;
	}

	i2c_set_clientdata(i2c, chip);

	ret = max20087_parse_regulators_dt(chip);
	if (ret < 0) {
		dev_err(chip->dev, "No regulators defined for the platform\n");
		return ret;
	}

	ret = regmap_read(chip->regmap, MAX20087_REG_ID, &data);
	if (ret < 0) {
		dev_err(chip->dev, "Failed to read DEVICE_ID reg: %d\n", ret);
		return ret;
	}

	data &= DEVICE_ID_MASK;
	switch (data) {
	case MAX20087_DEVICE_ID:
		/* Turn off all outputs */
		chip->chip_id = MAX20087_ID;
		ret = regmap_update_bits(chip->regmap,
					 MAX20087_REG_CONFIG,
					 MAX20087_EN_MASK, 0);
		if (ret < 0) {
			dev_err(chip->dev, "Failed to write ");
			dev_err(chip->dev, "MAX20087_REG_CONFIG reg: %d\n",
				ret);
		}
		break;
	default:
		dev_err(chip->dev, "Unsupported device id = 0x%x.\n", data);
		return -ENODEV;
	}

	chip->chip_irq = i2c->irq;
	if (chip->chip_irq != 0) {
		ret =
		devm_request_threaded_irq(chip->dev,
					  chip->chip_irq, NULL,
					  max20087_irq_handler,
					  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
					  "max20087", chip);
		if (ret != 0) {
			dev_err(chip->dev, "Failed to request IRQ: %d\n",
				chip->chip_irq);
			return ret;
		}
	} else {
		dev_warn(chip->dev, "No IRQ configured\n");
	}

	ret = max20087_regulator_init(chip);
	if (ret < 0)
		dev_err(chip->dev,
			"Failed to initialize regulator: %d\n", ret);

	ret = regmap_read(chip->regmap, MAX20087_REG_CONFIG, &data);
	if (ret < 0) {
		dev_err(chip->dev,
			"Failed to read MAX20087_REG_CONFIG reg: %d\n", ret);
		return ret;
	}
	dev_dbg(chip->dev, "reg CONFIG 0x%x\n", data);
	printk("%s: reg CONFIG 0x%x\n", __func__, data);

	return ret;
}

static const struct i2c_device_id max20087_i2c_id[] = {
	{"max20087", MAX20087_ID},
	{},
};

MODULE_DEVICE_TABLE(i2c, max20087_i2c_id);

static const struct of_device_id max20087_dt_ids[] = {
	{ .compatible = "max20087", .data = &max20087_i2c_id[0] },
	{},
};

MODULE_DEVICE_TABLE(of, max20087_dt_ids);

static struct i2c_driver max20087_regulator_driver = {
	.driver = {
		.name = "max20087",
		.of_match_table = of_match_ptr(max20087_dt_ids),
	},
	.probe = max20087_i2c_probe,
	.id_table = max20087_i2c_id,
};

module_i2c_driver(max20087_regulator_driver);

MODULE_AUTHOR("Watson Chow <watson.chow@avnet.com>");
MODULE_DESCRIPTION("MAX20087 Power Protector driver");
MODULE_LICENSE("GPL");

dts配置

max20087-regulator@28 {
        compatible = "max20087";
        reg = <0x28>;

        enable-gpios = <&tca_gpio 2 GPIO_ACTIVE_HIGH>;

        regulators {
			camvcc_0: OUT1 {
				regulator-name = "VOUT1";
				regulator-min-microvolt = <1200000>;
				regulator-max-microvolt = <1200000>;
				regulator-min-microamp	= <600000>;
				regulator-max-microamp	= <600000>;
			};
			camvcc_1: OUT2 {
				regulator-name = "VOUT2";
				regulator-min-microvolt = <1200000>;
				regulator-max-microvolt = <1200000>;
				regulator-min-microamp	= <600000>;
				regulator-max-microamp	= <600000>;
			};
			camvcc_2: OUT3 {
				regulator-name = "VOUT3";
				regulator-min-microvolt = <1200000>;
				regulator-max-microvolt = <1200000>;
				regulator-min-microamp	= <600000>;
				regulator-max-microamp	= <600000>;
			};
			camvcc_3: OUT4 {
				regulator-name = "VOUT4";
				regulator-min-microvolt = <1200000>;
				regulator-max-microvolt = <1200000>;
				regulator-min-microamp	= <600000>;
				regulator-max-microamp	= <600000>;
			};
        };
    };

2. regulator 框架

1.regulator概念及作用

Regulator,中文名翻译为“稳定器”,在电子工程中,是voltage regulator(稳压器)或者current regulator(稳流器)的简称,指可以自动维持恒定电压(或电流)的装置。一般电源管理芯片(Power Management IC)中会包含一个甚至多个regulator。

通常的作用是给电子设备供电。大多数regulator可以启用(enable)和禁用(disable)其输出,同时也可以控制其输出电压(voltage)和电流(current)。
在这里插入图片描述

从上图可以看出,input power会经过 regulator 转化为output power,regulator会做如下的约束:

  • Voltage control: 输入5V输出1.8V
  • Current limiting: 电流的输出最大为20MA
  • Power switch: 可以控制电压enable/disable

2.Linux Regulator Framework

  1. Linux Regulator Framework设计出主要是提供一个标准的内核接口来控制电压和电流调节器。目的是允许系统动态控制regulator power输出以节省能源延长电池寿命。这适用于voltage regulator和current regulator(其中电压和电流都是可控的)。
  2. Linux Regulator Framework分为四个部分,分别是machine,regulator,consumer,sys-class-regulator。

machine

machine可以理解为regulator在板级的硬件配置,使用regulator_init_data结构体代表regulator板级的配置。
machine的主要功能,是使用软件语言(struct regulator_init_data),静态的描述regulator在板级的物理现状,包括:
1)前级regulator(即该regulator的输出是另一个regulator的输入,简称supply regulator)和后级regulator(即该regulator的输入是其它regulator的输出,简称consumer regulator)。
2)该regulator的物理限制(struct regulation_constraints)


	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 */
	};

.supply_regulator: regulator的parent。用于级联regulator使用。
.constraints: 此regulator的约束,比如输出电压范围,输出电流范围等。
.num_consumer_supplies: 此regulator提供的consumer的个数,也就是控制外设的个数。
.consumer_supplies: 使用此结构确定regulator和consumer之间的联系。
.regulator_init: regulator注册时候的回调函数。
.driver_data: regulator_init回调函数的参数。


struct regulation_constraints {
 
	const char *name;
 
	/* voltage output range (inclusive) - for voltage control */
	int min_uV;
	int max_uV;
 
	int uV_offset;
 
	/* current output range (inclusive) - for current control */
	int min_uA;
	int max_uA;
 
	/* valid regulator operating modes for this machine */
	unsigned int valid_modes_mask;
 
	/* valid operations for regulator on this machine */
	unsigned int valid_ops_mask;
 
	/* regulator input voltage - only if supply is another regulator */
	int input_uV;
 
	/* regulator suspend states for global PMIC STANDBY/HIBERNATE */
	struct regulator_state state_disk;
	struct regulator_state state_mem;
	struct regulator_state state_standby;
	suspend_state_t initial_state; /* suspend state to set at init */
 
	/* mode to set on startup */
	unsigned int initial_mode;
 
	unsigned int ramp_delay;
	unsigned int enable_time;
 
	/* constraint flags */
	unsigned always_on:1;	/* regulator never off when system is on */
	unsigned boot_on:1;	/* bootloader/firmware enabled regulator */
	unsigned apply_uV:1;	/* apply uV constraint if min == max */
	unsigned ramp_disable:1; /* disable ramp delay */
};

.name: 描述该约束的名字。
.min_uV/max_uV: 最小/最大的输出电压。
.uV_offset: consumer看到的电源和实际电源之间的偏移值,用于电源补偿。
.min_uA/max_uA: 最小/最大的输出电流。
.valid_modes_mask: 该regulator支持的操作模式。
#define REGULATOR_MODE_FAST 0x1 //快速改变模式
#define REGULATOR_MODE_NORMAL 0x2 //正常模式,大多数驱动都使用这种模式
#define REGULATOR_MODE_IDLE 0x4 //设备在idle状态,regulator给设备提供服务
#define REGULATOR_MODE_STANDBY 0x8 //设备在standby状态,regulator给设备提供服务
.valid_ops_mask: 该regulator支持的操作。
#define REGULATOR_CHANGE_VOLTAGE 0x1 //该regulator可以改变电压
#define REGULATOR_CHANGE_CURRENT 0x2 //该regulator可以改变电流
#define REGULATOR_CHANGE_MODE 0x4 //该regulator可以改变mode
#define REGULATOR_CHANGE_STATUS 0x8 //该regulator可以改变状态,也就是enable/disable power
#define REGULATOR_CHANGE_DRMS 0x10 //该regulator可以动态该变mode
#define REGULATOR_CHANGE_BYPASS 0x20 //该regulator支持bypass mode
.input_uV: 表示该regulator的input是另一个regulator。
.state_disk/state_mem/state_standby: 代表该regulator的各种suspend状态。
.always_on: 是否在系统启动后一直使能。
.boot_on: 是否在boot阶段使能。
.apply_uV: 当min_uV=max_uV的时候时使用。
.ramp_delay: 改变电压到电源稳定后时间。因为硬件原因,改变电源后不能立刻就成功,其中需要有一定的延迟。
.enable_time: regulator的使能时间。

	aldo8: regulator-aldo8 {
		regulator-compatible = "lc,lc1160-aldo8";
		regulator-min-microvolt = <1750000>;
		regulator-max-microvolt = <3300000>;
		regulator-always-on = <1>;
		regulator-boot-on = <1>;
	};

egulator-name对应struct regulation_constraints中的name
regulator-min-microvolt对应struct regulation_constraints中的min_uV
regulator-max-microvolt对应struct regulation_constraints中的max_uV
regulator-always-on对应struct regulation_constraints中的always_on
regulator-boot-on对应struct regulation_constraints中的boot_on

1)在regulator注册前,调用of_get_regulator_init_data接口自行解析,该接口的实现如下

struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
						struct device_node *node)
{
	struct regulator_init_data *init_data;

	if (!node)
		return NULL;

	init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL);
	if (!init_data)
		return NULL; /* Out of memory? */

	of_get_regulation_constraints(node, &init_data);
	return init_data;
}

该接口有两个输入参数:设备指针,以及包含了DTS信息的node指针,它会分配一个struct regulator_init_data变量,并调用of_get_regulation_constraints解析DTS,把结果保存在该变量中。最后返回struct regulator_init_data变量的地址。
2)在regulator注册前, 多个电源可以由of_regulator_match调用of_get_regulator_init_data帮忙解析,该接口的实现如下

int of_regulator_match(struct device *dev, struct device_node *node,
		       struct of_regulator_match *matches,
		       unsigned int num_matches){
		for (i = 0; i < num_matches; i++) {
			struct of_regulator_match *match = &matches[i];
			if (match->of_node)
				continue;

			if (strcmp(match->name, name))
				continue;

			match->init_data =
				of_get_regulator_init_data(dev, child);
			if (!match->init_data) {
				dev_err(dev,
					"failed to parse DT for regulator %s\n",
					child->name);
				return -EINVAL;
			}
			match->of_node = child;
			count++;
			break;
		
}

regulator

regulator可以理解为regulator driver。主要作用有注册自己的regulator服务到regulator core framework中,给consumer提供服务。regulator driver通过regulator_register函数注册regulator operation到regulator core。其中第一个参数struct regulator_desc代表静态的regulator配置,所谓静态就是不再会改变的配置。第二个参数代表regulator的动态配置信息。
使用struct regulator_desc描述regulator的静态信息,包括:名字、supply regulator的名字、中断号、操作函数集(struct regulator_ops)、使用regmap时相应的寄存器即bitmap等等。
结构体如下:

struct regulator_desc {
	const char *name; 				//该regulator的名字
	const char *supply_name; 		// 该regulator parent的name,在级联时使用。
	const char *of_match;			// 在dt中指定的name。
	const char *regulators_node;		//定义在dt中的regulator name.
	int id;				//用于标识该regulator,也可以标识系统有多少个regulator.
	bool continuous_voltage_range;
						//表示regulator是否可以在电压约束范围内连续输出电压。
	unsigned n_voltages;			//通过ops.list_voltage函数获取可用的电压数量。
	const struct regulator_ops *ops;	//regulator的操作函数集合。
	int irq;						//该regulator的中断号。
	enum regulator_type type;		//代表当前regulator的类型。
	struct module *owner;		
 
	unsigned int min_uV;		//如果是线性mapp的话,使用最低的selector获取的电压。
	unsigned int uV_step;		//每个selector下电压增加step。
	unsigned int linear_min_sel;	//线性mapp下最小的selector。
	int fixed_uV;				// 固定电压。
	unsigned int ramp_delay;	//电压改变之后需要多久时间稳定下来。
 
	const struct regulator_linear_range *linear_ranges; // 电压的可能范围的常数表
	int n_linear_ranges; //  电压范围常数表的个数。
 
	const unsigned int *volt_table;// 电压的mapp表
 
	unsigned int vsel_reg;
	unsigned int vsel_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 enable_time;// regulator初始化所需要的时间。
 
	unsigned int off_on_delay;// 重新使能regulator的保护时间。
};

使用struct regulator_config,描述regulator的动态信息(所谓的动态信息,体现在struct regulator_config变量都是局部变量,因此不会永久保存),包括struct regulator_init_data指针、设备指针、enable gpio等等。此结构体如下:

struct regulator_config {
	struct device *dev;
	const struct regulator_init_data *init_data;
	void *driver_data;
	struct device_node *of_node;
	struct regmap *regmap;
 
	int ena_gpio;
	unsigned int ena_gpio_invert:1;
	unsigned int ena_gpio_flags;
};

.dev: struct device指针。
.init_data: 板级的相关初始化信息,通过解析DT,保存在此。
.dirver_data: 私有数据。
.of_node: dt相关的node。
.regmap: regulator register map。

当调用regulator_register函数之后,传入静态和动态参数之后,就会返回一个regulator_dev结构。此结构可以认为是regulator设备的一个抽象描述。

struct regulator_dev {
	const struct regulator_desc *desc;
	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 */
 
	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 */
	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;
};

.list: 所有的regulator链表。
.consumer: 此regulator下所有的consumer。
.notifier: 此regulator的通知链,用于给consumer通知event。
.supply: 该regulator的supply,级联时候使用。
.disable_work: 该regulator的延迟工作,用于延迟disable regulator。
.use_count/open_count:代表该regulator的使用计数。
.exclusive: 该regulator是否是唯一的标志。

consumer

consumer可以理解为regulator提供服务的对象。比如LCD使用regulator管理自己,就必须使用regulator core提供的regulator相关接口函数。regulator_get()/regulator_put()函数。
详细可以看Documentation/power/regulator/consumer.txt文档。
sysfs-class-regulator
regulator core framework通过sysfs文件系统导出了一些"voltage/current/opmode"相关的信息,此将很有帮忙监控设备的功耗使用情况。
详细信息可以看Documentation/ABI/testing/sysfs-class-regulator文档。

Regulator注册
Lc1860上使用的PMIC为lc1160芯片,包含了电源管理以及audio codec,除了电源管理功能外,还内置了多个regulator,例如dcdc、aldo、dldo等

具体的regulator注册在kernel\linux-3.10.y\drivers\comip\pmic\lc1160目录中,dcdc、ldo分别在lc1160-dcdc.c和lc1160-ldo.c中注册。
|lc1160_ldo_probe
|–lc1160_ldo_parse_dt解析dts得到pdata
|–|–of_regulator_match计算出找到定义在ldo_reg_matches中的name的个数
|–|–|–of_get_regulator_init_data 调用of_get_regulation_constraints解析dts得到init data
|–regulator_register该接口接受描述该regulator的两个变量的指针:struct regulator_desc和struct regulator_config,并分配一个新的数据结构(struct regulator_dev,从设备的角度描述regulator),并把静态指针(struct regulator_desc)和动态指针(struct regulator_config)提供的信息保存在其中。注册regulator返回regulator_dev,后续所有的regulator操作,都将以该变量为对象

regulator consumer interface

  1. 获取regulator/释放regulator
    regulator = regulator_get(dev, “Vcc”);
    regulator_put(regulator);

  2. Enable and disable
    int regulator_enable(regulator);
    int regulator_disable(regulator);
    int regulator_force_disable(regulator);

  3. 设置regulator的电流,获得regulator的电流状态
    int regulator_set_current_limit(regulator, min_uA, max_uA);
    int regulator_get_current_limit(regulator);

  4. regulator的模式设置,间接(通过负载),直接设置
    int regulator_set_optimum_mode(struct regulator *regulator, int load_uA);
    int regulator_set_mode(struct regulator *regulator, unsigned int mode);
    unsigned int regulator_get_mode(struct regulator *regulator);

core模块向用户空间提供的sysfs接口

regulator设备在内核中是以regulator class的形式存在的,regulator core通过class->dev_groups的方式,提供了一些默认的attribute,包括:

name,读取可以获取该regulator的名字;
num_users,读取可获取regulator的使用者数目;
type,读取可以获取该regulator的类型(voltage或者current)。

另外,如果regulator driver需要提供更多的attribute(如状态、最大/最小电压等等),可以调用add_regulator_attributes接口,主动添加。

Question

始终没有找到consumer_supplies和num_consumer_supplies在哪里提供的,//regulator_map_list

linux3.x下采用dts的方式建立设备树,自然给驱动带来很多改变。regulator也有很多变化

没有对regulator_consumer_supply进行初始化,那如何获得regulator指针,并对regulator进行电压设定呢?
通过regulator.txt我们发现,在regulator的optional properties中有
-supply: phandle to the parent supply /regulator node
看起来这个是用来标记父节点的。那么这个值又是如何使用的呢?才让设备能正确的获得regulator的呢?
在drivers/regulator/core.c中我们发现。
regulator_get->_regulator_get->regulaotr_dev_lookup->list_for_each_entry->of_get_regulator:
snprintf(prop_name, 32, “%s-supply”, supply);
regnode = of_parse_phandle(dev->of_node, prop_name, 0);
将我们传入的name,通过组合称name-supply的形式,再读取dts里的值,以此来获得我们需要的regulator的指针。
因此我们需要在dts里组织称类似
***-supply = <$dcdc1>
样子的属性,来表示所有设备的供电关系。

参考:
http://www.360doc.com/content/15/0708/16/10366845_483590831.shtml#
https://blog.csdn.net/bingqingsuimeng/article/details/8216782
http://www.wowotech.net/pm_subsystem/regulator_framework_overview.html
https://blog.csdn.net/longwang155069/article/details/53129378
http://blog.sina.com.cn/s/blog_a336aee70102v91l.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值