电源管理之pmu驱动分析
电源管理芯片可以为多设备供电,且这些设备电压电流有所不同。为这些设备提供的稳压器代码模型即为regulator。
说白了regulator就是稳压器,它提供电源供给.简单的可以gpio操作,高电平开电,低电平关电.一般的还包括电流值,
电压值等.
一般regulator有两种不同的电源,即:ldo和sd.
Ldo适合电压要求比较稳,但是功率不是很大的设备.
Sd适合功率要求比较大,但可以接受较小的纹波的设备.
除此之外pmu还可能集成,charger,battery, 音频功放等等.
首先我们分析pmu驱动的平台设备注册部分.
我以max77663这款pmu芯片为分析对象, cpu用的是nvidia的tegra3.
Pmu的板级初始化文件:kernel\arch\arm\mach-tegra\board-kai-power.c
主要代码如下:
#define PMC_CTRL 0x0
#define PMC_CTRL_INTR_LOW (1 << 17)
#define REBOOT_FLAG"rebooting"
#define DEVICE_PATH "/dev/block/platform/sdhci-tegra.3/by-name/UDE"
static structregulator_consumer_supply max77663_sd0_supply[] = {
REGULATOR_SUPPLY("vdd_cpu",NULL),
};
static structregulator_consumer_supply max77663_sd1_supply[] = {
REGULATOR_SUPPLY("vdd_core",NULL),
static struct regulator_consumer_supplymax77663_sd2_supply[] = {
REGULATOR_SUPPLY("vdd_gen1v8",NULL),
REGULATOR_SUPPLY("avdd_hdmi_pll",NULL),
REGULATOR_SUPPLY("avdd_usb_pll",NULL),
REGULATOR_SUPPLY("avdd_osc",NULL),
REGULATOR_SUPPLY("vddio_sys",NULL),
REGULATOR_SUPPLY("vddio_sdmmc","sdhci-tegra.3"),
REGULATOR_SUPPLY("pwrdet_sdmmc4",NULL),
REGULATOR_SUPPLY("vddio_uart",NULL),
REGULATOR_SUPPLY("pwrdet_uart",NULL),
REGULATOR_SUPPLY("vddio_bb",NULL),
REGULATOR_SUPPLY("pwrdet_bb",NULL),
REGULATOR_SUPPLY("vddio_lcd_pmu",NULL),
REGULATOR_SUPPLY("pwrdet_lcd",NULL),
REGULATOR_SUPPLY("vddio_audio",NULL),
REGULATOR_SUPPLY("pwrdet_audio",NULL),
REGULATOR_SUPPLY("vddio_cam",NULL),
REGULATOR_SUPPLY("pwrdet_cam",NULL),
REGULATOR_SUPPLY("vddio_sdmmc","sdhci-tegra.2"),
REGULATOR_SUPPLY("pwrdet_sdmmc3",NULL),
REGULATOR_SUPPLY("vddio_vi",NULL),
REGULATOR_SUPPLY("pwrdet_vi",NULL),
REGULATOR_SUPPLY("vcore_nand",NULL),
REGULATOR_SUPPLY("pwrdet_nand",NULL),
static structregulator_consumer_supply max77663_sd3_supply[] = {
REGULATOR_SUPPLY("vdd_ddr3l_1v35",NULL),
static structregulator_consumer_supply max77663_ldo0_supply[] = {
REGULATOR_SUPPLY("vdd_ddr_hs",NULL),
static structregulator_consumer_supply max77663_ldo1_supply[] = {
static structregulator_consumer_supply max77663_ldo2_supply[] = {
REGULATOR_SUPPLY("vdd_ddr_rx",NULL),
static structregulator_consumer_supply max77663_ldo3_supply[] = {
REGULATOR_SUPPLY("vmmc",NULL),
static structregulator_consumer_supply max77663_ldo4_supply[] = {
REGULATOR_SUPPLY("vdd_rtc",NULL),
static structregulator_consumer_supply max77663_ldo5_supply[] = {
REGULATOR_SUPPLY("vdd_sensor_2v8",NULL),
static structregulator_consumer_supply max77663_ldo6_supply[] = {
REGULATOR_SUPPLY("vddio_sdmmc","sdhci-tegra.0"),
REGULATOR_SUPPLY("pwrdet_sdmmc1",NULL),
static structregulator_consumer_supply max77663_ldo7_supply[] = {
REGULATOR_SUPPLY("avdd_dsi_csi",NULL),
REGULATOR_SUPPLY("pwrdet_mipi",NULL),
static struct regulator_consumer_supplymax77663_ldo8_supply[] = {
REGULATOR_SUPPLY("avdd_plla_p_c_s",NULL),
REGULATOR_SUPPLY("avdd_pllm",NULL),
REGULATOR_SUPPLY("avdd_pllu_d",NULL),
REGULATOR_SUPPLY("avdd_pllu_d2",NULL),
REGULATOR_SUPPLY("avdd_pllx",NULL),
static structmax77663_regulator_fps_cfg max77663_fps_cfgs[] = {
.time_period= FPS_TIME_PERIOD_DEF,
.time_period= FPS_TIME_PERIOD_DEF,
.time_period= FPS_TIME_PERIOD_DEF,
#define MAX77663_PDATA_INIT(_id,_min_uV, _max_uV, _supply_reg, \
_always_on, _boot_on, _apply_uV, \
_init_apply, _init_enable, _init_uV, \
_fps_src, _fps_pu_period, _fps_pd_period,_flags) \
staticstruct max77663_regulator_platform_data max77663_regulator_pdata_##_id = \
.valid_modes_mask= (REGULATOR_MODE_NORMAL | \
.valid_ops_mask= (REGULATOR_CHANGE_MODE | \
ARRAY_SIZE(max77663_##_id##_supply), \
.consumer_supplies= max77663_##_id##_supply, \
.supply_regulator= _supply_reg, \
.fps_pu_period= _fps_pu_period, \
.fps_pd_period= _fps_pd_period, \
.fps_cfgs= max77663_fps_cfgs, \
MAX77663_PDATA_INIT(sd2, 1800000, 1800000, NULL, 1, 0, 0,1, 1, -1,FPS_SRC_0, -1, -1, 0);
MAX77663_PDATA_INIT(sd3, 600000, 3387500, NULL, 1, 0, 0,1, 1, -1,FPS_SRC_0, -1, -1, 0);
MAX77663_PDATA_INIT(ldo2, 800000,3950000, NULL, 1, 0, 0,1, 1, -1, FPS_SRC_1, -1, -1, 0);
MAX77663_PDATA_INIT(ldo3, 800000,3950000, NULL, 1, 0, 0,1, 1, -1, FPS_SRC_1, -1, -1, 0);
MAX77663_PDATA_INIT(ldo5, 800000,2800000, NULL, 0, 0, 0,1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
MAX77663_PDATA_INIT(ldo6, 800000,3950000, NULL, 0, 0, 0,0, 0, -1, FPS_SRC_NONE, -1, -1, 0);
#define MAX77663_REG(_id, _data) \
.name= "max77663-regulator", \
.id= MAX77663_REGULATOR_ID_##_id, \
.platform_data= &max77663_regulator_pdata_##_data, \
.pdata_size= sizeof(max77663_regulator_pdata_##_data), \
static struct mfd_cellmax77663_subdevs[] = {
static structmax77663_gpio_config max77663_gpio_cfgs[] = {
.out_drv= GPIO_OUT_DRV_PUSH_PULL,
.out_drv= GPIO_OUT_DRV_PUSH_PULL,
.out_drv= GPIO_OUT_DRV_OPEN_DRAIN,
.out_drv= GPIO_OUT_DRV_OPEN_DRAIN,
.out_drv= GPIO_OUT_DRV_PUSH_PULL,
.out_drv= GPIO_OUT_DRV_PUSH_PULL,
.out_drv= GPIO_OUT_DRV_OPEN_DRAIN,
static structmax77663_platform_data max7763_pdata = {
.irq_base = MAX77663_IRQ_BASE,
.gpio_base = MAX77663_GPIO_BASE,
.flags=SLP_MONITORS_ENABLE|SLP_LPM_ENABLE,
.num_gpio_cfgs = ARRAY_SIZE(max77663_gpio_cfgs),
.gpio_cfgs = max77663_gpio_cfgs,
.num_subdevs = ARRAY_SIZE(max77663_subdevs),
.sub_devices = max77663_subdevs,
static struct i2c_board_info__initdata max77663_regulators[] = {
/*The I2C address was determined by OTP factory setting */
I2C_BOARD_INFO("max77663",0x3c),
.platform_data = &max7763_pdata,
static int __initkai_max77663_regulator_init(void)
void__iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
/*configure the power management controller to trigger PMU
pmc_ctrl= readl(pmc + PMC_CTRL);
writel(pmc_ctrl| PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
i2c_register_board_info(4,max77663_regulators,
ARRAY_SIZE(max77663_regulators));
static structregulator_consumer_supply fixed_reg_en_1v8_cam_supply[] = {
REGULATOR_SUPPLY("vdd_1v8_cam1",NULL),
static structregulator_consumer_supply fixed_reg_en_cam1_ldo_supply[] = {
REGULATOR_SUPPLY("vdd_cam1",NULL),
static structregulator_consumer_supply fixed_reg_en_3v3_sys_a01_supply[] = {
REGULATOR_SUPPLY("vdd_3v3",NULL),
REGULATOR_SUPPLY("vdd_3v3_devices",NULL),
REGULATOR_SUPPLY("debug_cons",NULL),
REGULATOR_SUPPLY("pwrdet_pex_ctl",NULL),
REGULATOR_SUPPLY("vddio_gmi",NULL),
static structregulator_consumer_supply fixed_reg_en_avdd_hdmi_usb_a01_supply[] = {
REGULATOR_SUPPLY("avdd_hdmi",NULL),
REGULATOR_SUPPLY("avdd_usb",NULL),
static structregulator_consumer_supply fixed_reg_en_vddio_vid_supply[] = {
REGULATOR_SUPPLY("vdd_hdmi_con",NULL),
static structregulator_consumer_supply fixed_reg_en_vdd_sdmmc1_supply[] = {
REGULATOR_SUPPLY("vddio_sd_slot","sdhci-tegra.0"),
static structregulator_consumer_supply fixed_reg_en_3v3_fuse_supply[] = {
REGULATOR_SUPPLY("vdd_fuse",NULL),
/* Macro for defining fixedregulator sub device data */
#define FIXED_SUPPLY(_name)"fixed_reg_"#_name
#define FIXED_REG(_id, _var,_name, _in_supply, _always_on, _boot_on, \
_gpio_nr,_active_high, _boot_state, _millivolts) \
staticstruct regulator_init_data ri_data_##_var = \
.supply_regulator= _in_supply, \
ARRAY_SIZE(fixed_reg_##_name##_supply), \
.consumer_supplies= fixed_reg_##_name##_supply, \
.valid_modes_mask= (REGULATOR_MODE_NORMAL | \
.valid_ops_mask= (REGULATOR_CHANGE_MODE | \
staticstruct fixed_voltage_config fixed_reg_##_var##_pdata = \
.supply_name= FIXED_SUPPLY(_name), \
.microvolts= _millivolts * 1000, \
.enabled_at_boot= _boot_state, \
.init_data= &ri_data_##_var, \
staticstruct platform_device fixed_reg_##_var##_dev = { \
.platform_data= &fixed_reg_##_var##_pdata, \
FIXED_REG(1, en_3v3_sys_a01, en_3v3_sys_a01, NULL,
1, 0, MAX77663_GPIO_BASE+ MAX77663_GPIO3, true, 1, 3300);
FIXED_REG(2,en_avdd_hdmi_usb_a01, en_avdd_hdmi_usb_a01, FIXED_SUPPLY(en_3v3_sys_a01),
0, 0, MAX77663_GPIO_BASE+ MAX77663_GPIO2, true, 0, 3300);
FIXED_REG(4, en_vddio_vid_a01, en_vddio_vid, NULL,
0, 0, TEGRA_GPIO_PB2, true, 0, 5000);
FIXED_REG(9, en_vdd_sdmmc1_a01, en_vdd_sdmmc1, FIXED_SUPPLY(en_3v3_sys_a01),
0, 0, TEGRA_GPIO_PC6, true, 0, 3300);
FIXED_REG(10, en_3v3_fuse_a01, en_3v3_fuse, FIXED_SUPPLY(en_3v3_sys_a01),
0, 0, TEGRA_GPIO_PC1, true, 0, 3300);
FIXED_REG(11, en_1v8_cam_a01, en_1v8_cam, NULL,
0, 0, TEGRA_GPIO_PS0, true, 0, 1800);
FIXED_REG(12, en_cam1_ldo_a01, en_cam1_ldo, FIXED_SUPPLY(en_3v3_sys_a01),
0, 0, TEGRA_GPIO_PR6, true, 0, 2800);
* Creating the fixed regulator device tables
#define ADD_FIXED_REG(_name) (&fixed_reg_##_name##_dev)
ADD_FIXED_REG(en_3v3_sys_a01), \
ADD_FIXED_REG(en_avdd_hdmi_usb_a01), \
ADD_FIXED_REG(en_vddio_vid_a01), \
ADD_FIXED_REG(en_vdd_sdmmc1_a01), \
ADD_FIXED_REG(en_3v3_fuse_a01), \
ADD_FIXED_REG(en_1v8_cam_a01),\
ADD_FIXED_REG(en_cam1_ldo_a01)
/* Gpio switch regulator platformdata for Kai A01 */
static struct platform_device*fixed_reg_devs_a01[] = {
static int __initkai_fixed_regulator_init(void)
structplatform_device **fixed_reg_devs;
tegra_get_board_info(&board_info);
fixed_reg_devs= fixed_reg_devs_a01;
nfixreg_devs= ARRAY_SIZE(fixed_reg_devs_a01);
for(i = 0; i < nfixreg_devs; ++i) {
structfixed_voltage_config *fixed_reg_pdata =
fixed_reg_devs[i]->dev.platform_data;
gpio_nr= fixed_reg_pdata->gpio;
printk("kai_fixed_regulator_initnfixreg_devs=%d\n",nfixreg_devs);
returnplatform_add_devices(fixed_reg_devs, nfixreg_devs);
subsys_initcall_sync(kai_fixed_regulator_init);
int __initkai_regulator_init(void)
void__iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
/*configure the power management controller to trigger PMU
pmc_ctrl= readl(pmc + PMC_CTRL);
writel(pmc_ctrl| PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
ret= kai_max77663_regulator_init();
首先看__init函数int __initkai_regulator_init(void)
àkai_max77663_regulator_init(); //max77663设备初始化函数
ài2c_register_board_info(4,max77663_regulators,ARRAY_SIZE(max77663_regulators)); //注册i2c硬件信息
->static structi2c_board_info __initdata max77663_regulators[] = {
/* The I2C address was determined byOTP factory setting */
I2C_BOARD_INFO("max77663",0x3c), //i2c地址
.irq =INT_EXTERNAL_PMU, //pmu中断
.platform_data = &max7763_pdata, //要传的平台数据,
static structmax77663_platform_data max7763_pdata = {
.gpio_base =MAX77663_GPIO_BASE,
.flags=SLP_MONITORS_ENABLE|SLP_LPM_ENABLE,
.num_gpio_cfgs =ARRAY_SIZE(max77663_gpio_cfgs),
.gpio_cfgs =max77663_gpio_cfgs,
.num_subdevs =ARRAY_SIZE(max77663_subdevs),
.sub_devices =max77663_subdevs,
.rtc_i2c_addr =0x68, //rtc i2c地址
max77663_gpio_cfgs函数主要是一些gpio引脚的初始化.
static structmfd_cell max77663_subdevs[] = {
#defineMAX77663_REG(_id, _data) \
.name ="max77663-regulator", \
.id = MAX77663_REGULATOR_ID_##_id, \
.platform_data =&max77663_regulator_pdata_##_data, \
.pdata_size =sizeof(max77663_regulator_pdata_##_data), \
先猜想一下,这个应该是赋值的地方. 接下来我们看MAX77663_PDATA_INIT是一个什么东东.
#defineMAX77663_PDATA_INIT(_id, _min_uV, _max_uV, _supply_reg, \
_always_on, _boot_on, _apply_uV, \
_init_apply, _init_enable, _init_uV, \
_fps_src, _fps_pu_period, _fps_pd_period,_flags) \
static struct max77663_regulator_platform_datamax77663_regulator_pdata_##_id = \
.valid_modes_mask= (REGULATOR_MODE_NORMAL | \
.valid_ops_mask =(REGULATOR_CHANGE_MODE | \
ARRAY_SIZE(max77663_##_id##_supply), \
.consumer_supplies =max77663_##_id##_supply, \
.supply_regulator =_supply_reg, \
.init_enable = _init_enable, \
.fps_pu_period = _fps_pu_period, \
.fps_pd_period = _fps_pd_period, \
.fps_cfgs = max77663_fps_cfgs, \
我们对比一个这两个东东: 1. .platform_data = &max77663_regulator_pdata_##_data,
2. staticstruct max77663_regulator_platform_data max77663_regulator_pdata_##_id =
明白了,程序执行MAX77663_REG(SD0, sd0),这一句的时候
会执行这个&max77663_regulator_pdata_##_data, (注意,data=sd0)
然后会执行max77663_regulator_pdata_##_id (_id传过来的是值是sd0)
#defineMAX77663_PDATA_INIT(_id, _min_uV, _max_uV, _supply_reg, \
_always_on, _boot_on, _apply_uV, \
_init_apply, _init_enable, _init_uV, \
_fps_src, _fps_pu_period, _fps_pd_period,_flags) \
static structmax77663_regulator_platform_data max77663_regulator_pdata_##_id =
别忘记了这个函数max77663_regulator_pdata_##_id ,传进来的_id=sd0
#defineMAX77663_PDATA_INIT(_id, _min_uV, _max_uV, _supply_reg, \
_always_on, _boot_on, _apply_uV, \
_init_apply, _init_enable, _init_uV, \
_fps_src, _fps_pu_period, _fps_pd_period,_flags) \
static struct max77663_regulator_platform_datamax77663_regulator_pdata_##_id = \
.valid_modes_mask= (REGULATOR_MODE_NORMAL | \
.valid_ops_mask =(REGULATOR_CHANGE_MODE | \
ARRAY_SIZE(max77663_##_id##_supply), \
.consumer_supplies =max77663_##_id##_supply, \
.supply_regulator =_supply_reg, \
.init_enable = _init_enable, \
.fps_pu_period = _fps_pu_period, \
.fps_pd_period = _fps_pd_period, \
.fps_cfgs = max77663_fps_cfgs, \
接下来我们需要关心消费者的问题,就是我们注册了regulator,谁去使用它呢?
注意这个函数: .consumer_supplies = max77663_##_id##_supply, \
又是一个宏, ##_id## ,我们知道了,刚刚我们传进来的_id是sd0,那么该函数就是:max77663_sd0_supply.
static struct regulator_consumer_supply max77663_sd0_supply[] ={
REGULATOR_SUPPLY("vdd_cpu",NULL),
static struct regulator_consumer_supply max77663_sd1_supply[] ={
REGULATOR_SUPPLY("vdd_core",NULL),
REGULATOR_SUPPLY("vdd_cpu", NULL),
为NULL那我们get的时候就不用关心消费者,只关心是哪一路regulator就好
其它路的regulator流程完全一样.
还有一点要特别指出的是:
#defineMAX77663_REG(_id, _data) \
{ \
.name ="max77663-regulator", \
.id = MAX77663_REGULATOR_ID_##_id, \
.platform_data =&max77663_regulator_pdata_##_data, \
.pdata_size =sizeof(max77663_regulator_pdata_##_data), \
}
这个宏它会多次被调用, 就是说他有多路regulator的平台设备name都一样, .name ="max77663-regulator".
那么我们可以大胆的设想一下,是不是平台驱动会probe很多次?
Yes,答案是肯定的,这个等我们分析平台驱动的时候就会知道.
到这里为止平台设备注册成功了.下一步需要关心驱动了.
Regulator驱动: kernel\drivers\regulator\max77663-regulator.c
kernel\include\linux\regulator\max77663-regulator.h
首先看init函数:
static int __init max77663_regulator_init(void)
{
returnplatform_driver_register(&max77663_regulator_driver);
}
注册了一个max77663_regulator_driver的平台驱动.
static struct platform_driver max77663_regulator_driver = {
.probe =max77663_regulator_probe,
.remove =__devexit_p(max77663_regulator_remove),
.driver = {
.name ="max77663-regulator",
.owner =THIS_MODULE,
},
};
重点关心probe函数: .probe = max77663_regulator_probe,
static int max77663_regulator_probe(struct platform_device*pdev)
{
structregulator_desc *rdesc;
structmax77663_regulator *reg;
int ret = 0;
if ((pdev->id< 0) || (pdev->id >= MAX77663_REGULATOR_ID_NR)) {
dev_err(&pdev->dev,"Invalid device id %d\n", pdev->id);
return-ENODEV;
}
rdesc =&max77663_rdesc[pdev->id];
reg =&max77663_regs[pdev->id];
reg->dev =&pdev->dev;
reg->pdata =dev_get_platdata(&pdev->dev);
dev_dbg(&pdev->dev,"probe: name=%s\n", rdesc->name);
ret =max77663_regulator_preinit(reg);
if (ret) {
dev_err(&pdev->dev,"probe: Failed to preinit regulator %s\n",
rdesc->name);
returnret;
}
reg->rdev =regulator_register(rdesc, &pdev->dev,
®->pdata->init_data, reg);
if(IS_ERR(reg->rdev)) {
dev_err(&pdev->dev,"probe: Failed to register regulator %s\n",
rdesc->name);
returnPTR_ERR(reg->rdev);
}
return 0;
}
这几条用于得到平台设备传过来的平台数据:
rdesc =&max77663_rdesc[pdev->id];
reg =&max77663_regs[pdev->id];
reg->dev =&pdev->dev;
reg->pdata =dev_get_platdata(&pdev->dev);
注意这里只传过来一路regulator的信息, 所以我们之前的猜想完全是对的,
就是这个probe函数会多次被调用,每一路regulator设备会调用一次.
那么是不是意味着这不是一个驱动而是一组驱动呢?
嗯,或许你可以这样理解.
这里我们分析一路就好了,其它的也就完全一样了.
其中这两句比较重要:
rdesc =&max77663_rdesc[pdev->id];
reg =&max77663_regs[pdev->id];
先看rdesc =&max77663_rdesc[pdev->id];
static struct regulator_descmax77663_rdesc[MAX77663_REGULATOR_ID_NR] = {
REGULATOR_DESC(DVSSD0,dvssd0),
#define REGULATOR_DESC(_id, _name) \
[MAX77663_REGULATOR_ID_##_id]= { \
.name =max77663_rails(_name), \
.id =MAX77663_REGULATOR_ID_##_id, \
static struct regulator_ops max77663_ldo_ops = {
.set_voltage =max77663_regulator_set_voltage,
.get_voltage =max77663_regulator_get_voltage,
.enable =max77663_regulator_enable,
.disable =max77663_regulator_disable,
.is_enabled =max77663_regulator_is_enabled,
.set_mode =max77663_regulator_set_mode,
.get_mode =max77663_regulator_get_mode,
看见了没有?这是有设置regulator的电压,和得到电压,以及enabled等函数.
好了,消费者怎么使用的问题有了头绪了,可是还有一个问题,硬件信息呢?
接下来我们看: reg =&max77663_regs[pdev->id];
static struct max77663_regulatormax77663_regs[MAX77663_REGULATOR_ID_NR] = {
REGULATOR_SD(SD0, SDX, SD0, 600000, 3387500, 12500),
REGULATOR_SD(DVSSD0,SDX, NONE, 600000, 3387500, 12500),
#define REGULATOR_LDO(_id, _type, _min_uV, _max_uV, _step_uV) \
[MAX77663_REGULATOR_ID_##_id]= { \
.id =MAX77663_REGULATOR_ID_##_id, \
.type =REGULATOR_TYPE_LDO_##_type, \
.addr= MAX77663_REG_##_id##_CFG, \
.addr= MAX77663_REG_##_id##_CFG2, \
.addr= MAX77663_REG_FPS_##_id, \
.regulator_mode= REGULATOR_MODE_NORMAL, \
.power_mode= POWER_MODE_NORMAL, \
.power_mode_mask= LDO_POWER_MODE_MASK, \
.power_mode_shift= LDO_POWER_MODE_SHIFT, \
好了,这就是要设置的硬件信息了, 电压值, 寄存器地址都有了.
.addr= MAX77663_REG_##_id##_CFG, \
.addr= MAX77663_REG_##_id##_CFG2, \
.addr= MAX77663_REG_FPS_##_id, \
rdesc =&max77663_rdesc[pdev->id];
reg =&max77663_regs[pdev->id];
这两个值好像有着某种对应关系, 一个可以提供操作函数,一个可以提供硬件的操作信息.那他们俩合在一起的话驱动不就完整了吗?
ret = max77663_regulator_preinit(reg);
staticint max77663_regulator_preinit(struct max77663_regulator *reg)
struct max77663_regulator_platform_data*pdata = _to_pdata(reg);
struct device *parent =_to_parent(reg);
for (i = 0; i <= FPS_REG; i++) {
ret = max77663_read(parent,reg->regs[i].addr,
"preinit:Failed to get register 0x%x\n",
if (reg->regs[FPS_REG].addr ==MAX77663_REG_FPS_NONE)
reg->fps_src =(reg->regs[FPS_REG].val & FPS_SRC_MASK)
dev_dbg(reg->dev, "preinit:initial fps_src=%s\n",
max77663_regulator_get_power_mode(reg);
/* Check Chip Identification */
ret = max77663_read(parent,MAX77663_REG_CID5, &val, 1, 0);
dev_err(reg->dev,"preinit: Failed to get register 0x%x\n",
/* If metal revision is less thanrev.3,
* set safe_down_uV for stable down scaling. */
if ((reg->type == REGULATOR_TYPE_SD)&&
((val &CID_DIDM_MASK) >> CID_DIDM_SHIFT) <= 2)
reg->safe_down_uV =SD_SAFE_DOWN_UV;
ret =max77663_regulator_set_fps_cfgs(reg, pdata->fps_cfgs,
dev_err(reg->dev,"preinit: Failed to set FPSCFG\n");
/* N-Channel LDOs don't supportLow-Power mode. */
if ((reg->type ==REGULATOR_TYPE_LDO_N) &&
/* To prevent power rail turn-off whenchange FPS source,
* it must set power mode to NORMAL beforechange FPS source to NONE
* from SRC_0, SRC_1 and SRC_2. */
if ((reg->fps_src != FPS_SRC_NONE)&& (pdata->fps_src == FPS_SRC_NONE)
&&(reg->power_mode != POWER_MODE_NORMAL)) {
val = (pdata->flags &GLPM_ENABLE) ?
POWER_MODE_GLPM : POWER_MODE_NORMAL;
ret =max77663_regulator_set_power_mode(reg, val);
dev_err(reg->dev,"preinit: Failed to "
"setpower mode to POWER_MODE_NORMAL\n");
ret =max77663_regulator_set_fps_src(reg, pdata->fps_src);
dev_err(reg->dev,"preinit: Failed to set FPSSRC to %d\n",
ret = max77663_regulator_set_fps(reg);
dev_err(reg->dev,"preinit: Failed to set FPS\n");
ret =max77663_regulator_do_set_voltage(reg, pdata->init_uV,
dev_err(reg->dev,"preinit: Failed to set voltage to "
val = (pdata->flags &GLPM_ENABLE) ?
POWER_MODE_GLPM : POWER_MODE_NORMAL;
ret =max77663_regulator_set_power_mode(reg, val);
"preinit:Failed to set power mode to %d\n", val);
if (reg->type == REGULATOR_TYPE_SD){
if (pdata->flags &SD_SLEW_RATE_MASK) {
if (pdata->flags& SD_SLEW_RATE_SLOWEST)
val |=(SD_SR_13_75 << SD_SR_SHIFT);
else if(pdata->flags & SD_SLEW_RATE_SLOW)
val |=(SD_SR_27_5 << SD_SR_SHIFT);
else if(pdata->flags & SD_SLEW_RATE_FAST)
val |=(SD_SR_55 << SD_SR_SHIFT);
val |=(SD_SR_100 << SD_SR_SHIFT);
if (pdata->flags &SD_FORCED_PWM_MODE)
if (pdata->flags &SD_FSRADE_DISABLE)
ret =max77663_regulator_cache_write(reg,
reg->regs[CFG_REG].addr,mask, val,
"Failedto set register 0x%x\n",
if ((reg->id ==MAX77663_REGULATOR_ID_SD0)
&&(pdata->flags & EN2_CTRL_SD0)) {
ret =max77663_regulator_set_power_mode(reg, val);
"Failedto set power mode to %d for "
ret =max77663_regulator_set_fps_src(reg, FPS_SRC_NONE);
"Failedto set FPSSRC to FPS_SRC_NONE "
if ((reg->id ==MAX77663_REGULATOR_ID_LDO4)
&&(pdata->flags & LDO4_EN_TRACKING)) {
ret = max77663_write(parent,MAX77663_REG_LDO_CFG3, &val, 1, 0);
"Failedto set register 0x%x\n",
structmax77663_regulator_platform_data *pdata = _to_pdata(reg);
struct device*parent = _to_parent(reg);
第一句是得到平台设备数据,在平台设备注册的时候也有一个max77663_regulator_platform_data哦,可别忘记了.
第二句是得到一个父regulator,你的regulator可以给子的regulator供电嘛. 好像也没看见怎么使用,不用管了.
for (i = 0; i<= FPS_REG; i++) {
ret =max77663_read(parent, reg->regs[i].addr,
"preinit:Failed to get register 0x%x\n",
if(reg->regs[FPS_REG].addr == MAX77663_REG_FPS_NONE)
reg->fps_src= (reg->regs[FPS_REG].val & FPS_SRC_MASK)
dev_dbg(reg->dev,"preinit: initial fps_src=%s\n",
max77663_regulator_get_power_mode(reg);
Pmu珍对某个cpu出厂都会预设一组参数进去的,保证cpu最低限度的正常上电时序.
ret = max77663_regulator_set_fps_cfgs(reg,pdata->fps_cfgs,pdata->num_fps_cfgs);
static int max77663_regulator_set_fps_cfgs(structmax77663_regulator *reg,
structmax77663_regulator_fps_cfg *fps_cfgs,
ret =max77663_regulator_set_fps_cfg(reg, &fps_cfgs[i]);
ret = max77663_regulator_set_power_mode(reg, val);
ret = max77663_regulator_set_fps_src(reg, pdata->fps_src);
ret = max77663_regulator_set_fps(reg);
ret = max77663_regulator_do_set_voltage(reg,pdata->init_uV,pdata->init_uV);
不想看了,写的很长,但无非全是些初始化操作, 什么config寄存器呀,频率寄存器呀,电压寄存器呀.
reg->rdev =regulator_register(rdesc, &pdev->dev,®->pdata->init_data,reg);
rdesc =&max77663_rdesc[pdev->id];
reg =&max77663_regs[pdev->id];
这两个结构合作了, 一个有硬件,寄存器配制信息,一个有opration操作函数,驱动完整了.
static struct regulator_ops max77663_ldo_ops = {
.set_voltage =max77663_regulator_set_voltage,
.get_voltage =max77663_regulator_get_voltage,
.enable =max77663_regulator_enable,
.disable =max77663_regulator_disable,
.is_enabled =max77663_regulator_is_enabled,
.set_mode =max77663_regulator_set_mode,
.get_mode =max77663_regulator_get_mode,
到此为止,流程全通了,实现这些函数应该没难度,和平常写函数的思路大同小异.
其实max77663驱动除了,regulator驱动部分还有.
Regulator驱动: kernel\drivers\regulator\max77663-regulator.c
kernel\include\linux\regulator\max77663-regulator.h
rtc驱动: kernel\drivers\rtc\rtc-max77663.c
max77663 core驱动: kernel\drivers\mfd\max77663-core.c
kernel\linux\mfd\max77663-core.h
细心的人一定发现了,我们在平台设备注册的时候有两个i2c地址,
其中一个是regulator的i2c地址,另一个则是rtc的i2c地址.
max77663 Core驱动其实是包括了一些regulator的具体硬件最终的操作, 我们的opration的最终操作函数会调用到里面.
最后还要就是regulator怎么使用? 我们注册了regulator,也初始化了它,还注册了opration函数.但并没有消费者真正去使用它.
记得我们刚学linux驱动的时候, 有一个file_opration函数,
可是我们这里的opration函数不是这样的,他是给其它模块使用的. 即:其它驱动程序使用.
比如:触摸屏它使用了一路regulator,他在机器suspend的时候要求关电(或者说低电),在机器开屏的时候才要求上电使用.
static struct regulator *nabi2_dsi_reg = NULL;
nabi2_dsi_reg = regulator_get(NULL, "avdd_dsi_csi");
static struct regulator_consumer_supply max77663_ldo7_supply[] ={
REGULATOR_SUPPLY("avdd_dsi_csi",NULL),
REGULATOR_SUPPLY("pwrdet_mipi",NULL),
regulator_enable(nabi2_dsi_reg);
regulator_disable(nabi2_dsi_reg);
int regulator_get_voltage(struct regulator *regulator)
int regulator_set_current_limit(struct regulator *regulator,intmin_uA, int max_uA)
转载于:https://www.cnblogs.com/biglucky/p/4059386.html
电源管理之pmu驱动分析相关推荐
- 电源管理芯片MAX17048驱动
电源管理芯片MAX17048驱动 芯片功能 电路 配置 代码 关于电量补偿 最近调试MAX17048,做个简单的记录. 基于STM32H743 芯片功能 MAX17048为小尺寸.微功耗电池电量计,用 ...
- Windows XP电源管理及注册表分析
http://www.cnblogs.com/ziwuge/archive/2011/10/04/2199141.html 注册表: 注册表中电源选项的关键字为"PowerCfg" ...
- Windows CE设备驱动开发之电源管理
4.7电源管理 电源管理模块管理设备电源,从而全面改进操作系统的电源使用效率:它所有设备的电源使用,同时能与不支持电源管理的应用程序及驱动程序共存. 使用电源管理可以有效的减少目标设备的电源消耗,同时 ...
- 关闭linux服务器电源,linux关闭ACPI电源管理模块
一.运行环境 # cat /etc/redhat-release CentOS release 6.2 (Final) # uname -a Linux web-server- 2.6.-.el6.x ...
- WinCE电源管理的简单介绍
电源管理的目的是节能,基本的节能方法是使系统适时的进出休眠状态.比如用户按下On/Off按钮,或者监视用户活动的定时器超时,或者应用呼叫api都可以使得系统休眠,用户再次按下On/Off或者其他唤醒中 ...
- Windows CE的电源管理
Windows CE的基本电源管理功能 在所有版本的Windows CE操作系统中,图形.视窗和事件子系统(GWES)在电源管理方面都发挥了关键作用.这是因为早期版本的电源管理功能是由用户的活动所驱动 ...
- android 休眠唤醒驱动流程分析,Android 电源管理——gotosleep和userActivity关注
一.Android power management应用层分析 Android提供了android.os.PowerManager类,该类用于控制设备的电源状态的切换. 该类对外有三个接口函数: 1. ...
- 电源管理与驱动设计笔记
关注同名微信公众号"混沌无形",有趣好文! 1.电源管理的功能:具备电压过高保护.电流过大保护.电量监测.过放保护等功能---->自主充电 2.一款清洁机器人的运动控制系统方 ...
- Android电源管理分析
Android电源管理 1.电源管理服务–PowerManagerService PowerManagerService提供Android系统的电源管理服务,主要功能是控制系统的待机状态,控制显示 ...
最新文章
- 哲学是什么?(选自:苏菲的世界)
- 设置CentOS7的grub密码
- mysql relay log_windows下mysql主从出现Failed to open the relay log(relay_log_pos 248)解决办法...
- 基本数据类型转换 || 自动类型转换与强制类型转换
- 信息系统项目管理师-信息安全管理考点笔记
- python websocket server_Python Websocket服务端
- Spring(四)——AOP、Spring实现AOP、Spring整合Mybatis、Spring中的事务管理
- css固定gridview的表头
- SAP CRM WebClient UI Abstract Page的Selenium实现
- html5标签详解,HTML5中figcaption标签用法详解
- OllyDBG 入门系列(二)-字串参考
- Python--JavaScript的对象
- mac osx 系统 brew install hadoop 安装指南
- 建议能在园子里面发布Silverlight2.0应用
- Java代码生成器简介、原理、开发流程和Demo
- 程序员确实更容易秃....
- 看了这篇文章,终于知道怎么区分DV、OV、EV证书了
- python动画精灵_Python小课堂第18课:如何使用Pygame做动画精灵和碰撞检测
- 苹果手机验真假_朋友说他用手机观察细胞结构,一开始我还以为是开玩笑的……...
- 总结:Web3用户体验的四个层