CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址: https://hceng.cn/2018/01/18/Linux电源管理/#more
探究Linux电源管理模型,并为Tiny4412的LCD驱动添加电源管理。
这是2018的第一篇博客,选了一个几乎没有接触过的角度作为开篇,希望2018年学习更多,记录更多。
以往接触的Linux驱动,没遇到使用电池供电的情况,因此几乎没关注电源的管理。
然而实际中,不少使用电池供电的硬件平台,例如手机、POS机等,就需要对电源进行管理,比如在不使用设备的时候,休眠屏幕省电。
Linux电源管理模型有两种:系统睡眠模型suspend和Runtime电源管理模型。
On (on) S0 - Working
Standby (standby) S1 - CPU and RAM are powered but not executed
Suspend to RAM (mem) S3 - RAM is powered and the running content is saved to RAM
Suspend to Disk, Hibernation (disk) S4 - All content is saved to Disk and power down
S3 aka STR(suspend to ram),挂起到内存,简称待机。计算机将目前的运行状态等数据存放在内存,关闭硬盘、外设等设备,进入等待状态。此时内存仍然需要电力维持其数据,但整机耗电很少。恢复时计算机从内存读出数据,回到挂起前的状态,恢复速度较快。对DDR的耗电情况进行优化是S3性能的关键,大多数手持设备都是用S3待机。
S4 aka STD(suspend to disk),挂起到硬盘,简称休眠。把运行状态等数据存放在硬盘上某个文件或者某个特定的区域,关闭硬盘、外设等设备,进入关机状态。此时计算机完全关闭,不耗电。恢复时计算机从休眠文件/分区中读出数据,回到休眠前的状态,恢复速度较慢。
系统休眠模型给我的感觉是以整机角度进行省电。
S3类似电脑的睡眠,在教长时间不使用电脑后,电脑黑屏,再次敲击键盘迅速显示桌面,原来的工作内容仍不变。
S4类似电脑的休眠,在长时间不使用电脑后,电脑黑屏,再次敲击键盘无反应,按下电源键,开机,原来的工作内容仍不变。
对于嵌入式设备,更多的是使用S3,将数据暂时放在内存里,以实现快速恢复,就像手机的电源键按下黑屏,再次按下迅速亮屏。
在Linux中,通过cat /sys/power/state
可以得知当前设备支持的节能模式,一般情况有如下选项:
- freeze:不涉及具体的Hardware或Driver,只是冻结所有的进程,包括用户空间进程及内核线程,能节省的能量较少,使用场景不多;
需要设置以上模式,只需echo mem > /sys/power/state
即可。
Runtime电源管理模型给我的感觉是以模块角度进行省电。
某种程度上是“高内聚和低耦合”的体现。
每个设备(包括CPU)都处理好自身的电源管理工作,尽量以最低的能耗完成交代的任务,尽量在不需要工作的时候进入低功耗状态,尽量不和其它模块有过多耦合。每个设备都是最节省的话,整个系统一定是最节省的。
suspend的流程还是挺复杂的,向/sys/power/state
写入命令后再到唤醒,将进行以下流程:
对源码进行分析,其休眠过程如下:
驱动程序里休眠相关的电源管理函数的调用过程:prepare—>suspend—>suspend_late—>suspend_noirq
对源码进行分析,其唤醒过程如下:
驱动程序里唤醒相关的电源管理函数的调用过程:resume_noirq—>resume_early—>resume->complete
对于驱动程序,我们主要关心Device PM
(针对每一个驱动)和少量Platform dependent PM
(针对CPU芯片相关)的内容。
首先将suspend功能加入内核:
Power management options --->
[*] Suspend to RAM and standby
这里默认是勾选上了的,就不管了。
进入Tiny4412内核,尝试休眠echo mem > /sys/power/state
,系统提示**No wake-up sources!
**。
可见,要进入休眠,必须要有唤醒源,没有唤醒源,休眠也没有意义。
唤醒源最常见的就是按键中断,就如同手机进入锁屏状态下,按下电源键唤醒一样,因此先写一个按键驱动。
原理图:
底板上有四个按键,分别连在GPX3_2、GPX3_3、GPX3_4、GPX3_5,引脚状态常高,按键按下变低电平。
设备树:
button_interrupt: button_interrupt {
compatible = "tiny4412,button_interrupt";
tiny4412,gpx3_2 = <&gpx3 2 GPIO_ACTIVE_HIGH>;
tiny4412,gpx3_3 = <&gpx3 3 GPIO_ACTIVE_HIGH>;
tiny4412,gpx3_4 = <&gpx3 4 GPIO_ACTIVE_HIGH>;
tiny4412,gpx3_5 = <&gpx3 5 GPIO_ACTIVE_HIGH>;
};
//设置为中断唤醒源
irq_set_irq_wake(irq, 1);
#ifdef CONFIG_PM_SLEEP
static int usb4604_i2c_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct usb4604 *hub = i2c_get_clientdata(client);
usb4604_switch_mode(hub, USB4604_MODE_STANDBY);
return 0;
}
static int usb4604_i2c_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct usb4604 *hub = i2c_get_clientdata(client);
usb4604_switch_mode(hub, hub->mode);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
usb4604_i2c_resume);
加入中断源后,休眠过程不再提示No wake-up sources!
,看样子休眠应该成功了。
此时,按下按键,板子并未唤醒,琢磨了一阵,初步怀疑有以下原因:
这里先卡住,继续后面。
前面的suspend流程分析里面,
在冻结APP之前,使用pm_notifier_call_chain(PM_SUSPEND_PREPARE)
来通知驱动程序;
在重启APP之后,使用pm_notifier_call_chain(PM_POST_SUSPEND)
来通知驱动程序;
因此,如果驱动程序有事情在上述时机要处理,可以使用notifier
机制。
使用步骤:
a.定义notifier_block结构体
static struct notifier_block lcd_pm_notif_block = {
.notifier_call = lcd_suspend_notifier,
};
b.notifier操作函数
static int lcd_suspend_notifier(struct notifier_block *nb, unsigned long event, void *dummy)
{
switch (event) {
case PM_SUSPEND_PREPARE:
printk("lcd suspend notifiler test: PM_SUSPEND_PREPARE\n");
return NOTIFY_OK;
case PM_POST_SUSPEND:
printk("lcd suspend notifiler test: PM_POST_SUSPEND\n");
return NOTIFY_OK;
default:
return NOTIFY_DONE;
}
}
c.注册notifier
在驱动init()或probe()里注册:
ret = register_pm_notifier(&lcd_pm_notif_block);
if(ret) {
printk("failed to register pm notifier.\n");
return -EINVAL;
}
在前面LCD驱动上修改lcd_drv.c
,测试如下:
前面的notifier
只是通知,在冻结APP之前和重启APP之后通知,
而电源管理应该刚好相反,是在冻结APP之后和重启APP之前对驱动的电源进行控制,
这就需要suspend
和resume
来实现。
platform_driver
里的driver
里添加pm
结构体:static struct platform_driver lcd_driver =
{
.driver = {
.name = "lcd_s702",
.pm = &lcd_pm,
.of_match_table = of_match_ptr(lcd_dt_ids),
},
.probe = lcd_probe,
.remove = lcd_remove,
};
pm
成员函数:static struct dev_pm_ops lcd_pm = {
.suspend = s702_lcd_suspend,
.resume = s702_lcd_resume,
};
c.编写成员函数:
{% codeblock lang:c %}
static int s702_lcd_suspend(struct device *dev)
{
//lcd休眠操作
//Direct Off: ENVID and ENVID_F are set to “0” simultaneously.
unsigned int temp;
printk(“enter %s\n”, func);
temp = readl(lcd_regs_base + VIDCON0);
temp &= ~(0x01 << 1 | 0x01 << 0);
writel(temp, lcd_regs_base + VIDCON0);
return 0;
}
static int s702_lcd_resume(struct device *dev)
{
//lcd唤醒操作
//Display On: ENVID and ENVID_F are set to “1”.
unsigned int temp;
printk("enter %s\n", __func__);
temp = readl(lcd_regs_base + VIDCON0);
writel(temp | (0x01 << 1) | (0x01 << 0), lcd_regs_base + VIDCON0);
return 0;
}
{% endcodeblock %}
这里只是简单的关闭/打开显示,理论上的操作应该是:
休眠时先备份所有LCD相关寄存器,恢复时再恢复所有寄存器,以及其它可能操作,比如重新开启时钟等。
同理,因为LCD显示和backlight是分开的,因此需要在backlight里也进行类似操作。
前面的suspend系统睡眠模型是将整个系统进行休眠,但如果需要在系统运行时,单独对某个模块进行休眠,就需要Runtime电源管理模型,这两个模型互相协作,才能最大的发挥电源管理的效果。
Runtime电源管理模型的原理比较简单,就是计数,
当该设备驱动被使用时就加1,放弃使用时就减1,
计数大于1时,就打开该设备的电源,等于0时就关闭电源。
Runtime PM相关的函数:
a. 使能/禁止 Runtime PM:pm_runtime_enable
/ pm_runtime_disable
(修改disable_depth变量)
b. 增加计数/减少计数:pm_runtime_get_sync
/ pm_runtime_put_sync
(修改usage_count变量)
c. 回调函数 暂停/恢复/空闲:runtime_suspend
/ runtime_resume
/ runtime_idle
调用pm_runtime_get_sync
去增加使用次数以及恢复的流程如下:
调用pm_runtime_put_sync
去减少使用次数以及暂停的流程如下:
前面的两个流程,只看到了runtime_resume
和runtime_idle
的调用,没有看到runtime_suspend
,
实际上,如果设备不提供runtime_idle
, 则最终会调用runtime_suspend
。
首先将Runtime功能加入内核,但本内核4.13.9里没找到相关选项,应该默认已经加入到内核里面了。
调用方式一:
驱动程序提供接口, APP来调用。
在驱动函数的open()
、close()
里,增加和减少引用计数。
APP调用驱动的时候就能相应的恢复、暂停设备。
调用方式二:
直接操作应用层文件:
恢复:
echo on > /sys/devices/.../power/control
流程:control_store(drivers\base\power\sysfs.c) -> pm_runtime_forbid -> atomic_inc -> rpm_resume
暂停:
echo auto > /sys/devices/.../power/control
流程:control_store(drivers\base\power\sysfs.c) -> pm_runtime_allow -> atomic_dec_and_test -> rpm_idle
platform_driver
里的driver
里添加pm
结构体:(和前面的一样,这里就无需操作)static struct platform_driver lcd_driver =
{
.driver = {
.name = "lcd_s702",
.pm = &lcd_pm,
.of_match_table = of_match_ptr(lcd_dt_ids),
},
.probe = lcd_probe,
.remove = lcd_remove,
};
pm
成员函数:static struct dev_pm_ops lcd_pm =
{
.suspend = s702_lcd_suspend,
.resume = s702_lcd_resume,
.runtime_suspend = s702_lcd_suspend,
.runtime_resume = s702_lcd_resume,
};
添加runtime_suspend
和runtime_resume
,runtime和suspend的暂停配置是一样的,直接使用前面的。
c.编写成员函数:(和前面的一样,这里就无需操作)
{% codeblock lang:c %}
static int s702_lcd_suspend(struct device *dev)
{
//lcd休眠操作
//Direct Off: ENVID and ENVID_F are set to “0” simultaneously.
unsigned int temp;
printk(“enter %s\n”, func);
temp = readl(lcd_regs_base + VIDCON0);
temp &= ~(0x01 << 1 | 0x01 << 0);
writel(temp, lcd_regs_base + VIDCON0);
return 0;
}
static int s702_lcd_resume(struct device *dev)
{
//lcd唤醒操作
//Display On: ENVID and ENVID_F are set to “1”.
unsigned int temp;
printk("enter %s\n", __func__);
temp = readl(lcd_regs_base + VIDCON0);
writel(temp | (0x01 << 1) | (0x01 << 0), lcd_regs_base + VIDCON0);
return 0;
}
{% endcodeblock %}
pm_runtime_set_active()
来修改它的状态,pm_runtime_enable()
来使能Runtime PM。在probe()
函数的后面添加:
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
反之,还要在remove()
里禁止:
pm_runtime_disable(&pdev->dev);
e.修改计数:
一般在open()
和release()
里面增加和减少引用计数:
{% codeblock lang:c %}
static int s702_lcd_open(struct fb_info *info, int user)
{
struct device *dev = info->dev;
int ret;
printk(“enter %s\n”, func);
ret = pm_runtime_get_sync(dev);
if (ret < 0 && ret != -EACCES)
{
pm_runtime_put_sync(dev);
return ret;
}
return 0;
}
static int s702_lcd_release(struct fb_info *info, int user)
{
struct device *dev = info->dev;
printk(“enter %s\n”, func);
pm_runtime_put_sync(dev);
return 0;
}
static struct fb_ops tiny4412_lcdfb_ops =
{
.owner = THIS_MODULE,
.fb_setcolreg = cfb_setcolreg, //设置调色板,实现伪颜色表
.fb_fillrect = cfb_fillrect, //填充矩形
.fb_copyarea = cfb_copyarea, //数据复制
.fb_imageblit = cfb_imageblit, //图形填充
.fb_open = s702_lcd_open,
.fb_release = s702_lcd_release
};
{% endcodeblock %}
insmod backlight_drv.ko
,运行背光应用程序设置亮度./app 200
;insmod lcd_drv.ko
,运行图片显示应用程序jpg_rgb显示图像./jpg_rgb cq.jpg
,结果并没有显示图像,echo on > /sys/devices/platform/11c00000.lcd_s702/power/control
才正常显示图像。上述流程中,运行图片显示应用程序时,先open()
了一次,引用计数加1,程序调用完又马上close
,引用计数减1,导致看不到显示,
重新操作/sys/devices/platform/11c00000.lcd_s702/power/control
就立即显示了图像。
对于正常的使用情景是,运行应用程序,立即图片显示,然后维持显示一段时间,如果有操作继续显示,没有的话再自己熄灭。
因此,想要实现上面的功能,还需要加入自动休眠。
在之前的probe()
中加入pm_runtime_use_autosuspend()
:
{% codeblock lang:c %}
//Runtime
pm_runtime_use_autosuspend(&pdev->dev);//add autosleep
pm_runtime_set_autosuspend_delay(&pdev->dev, 5000);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
{% endcodeblock %}
同时,release()
也要修改:
{% codeblock lang:c %}
static int s702_lcd_open(struct fb_info *info, int user)
{
struct device *dev = info->dev;
int ret;
printk("enter %s\n", __func__);
ret = pm_runtime_get_sync(dev);
if (ret < 0 && ret != -EACCES)
{
pm_runtime_put_sync(dev);
return ret;
}
return 0;
}
static int s702_lcd_release(struct fb_info *info, int user)
{
struct device *dev = info->dev;
printk("enter %s\n", __func__);
//pm_runtime_put_sync(dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_sync_autosuspend(dev);
return 0;
}
{% endcodeblock %}
此时,加载驱动后,运行应用程序,屏幕显示,5s后,屏幕自动熄灭,再次运行程序或者修改control
来重新显示。
通过函数pm_runtime_set_autosuspend_delay()
或修改echo xx > /sys/devices/.../power/autosuspend_delay_ms
来修改自动休眠时间。
完整代码见Github。
前面的两个电源管理模型偏“软”,regulator系统偏“硬”,
在复杂的单板中,有专门的电源管理芯片控制各个模块电源,regulator系统就是为这个电源芯片编写驱动,实现电源管理。
①Regulator(稳定器):指可以自动维持恒定电压(voltage)或电流(current)的装置,一般指电源芯片。在嵌入式设备中,基本上每一种电压,都是经过regulator输出的;
②③Consumer(使用者):使用电源的装置,Regulator是给Consumer供电的;
④Machine(单板):使用软件语言(struct regulator_init_data),静态的描述regulator在板级的物理现状,包含:
a.级联关系:Regulator A的输出是Regulator B的输入,Regulator A就是Supply regulator,B是Consumer regulator;
b.约束限制:Regulator Constraints,比如电压/电流最大值/最小值、允许的操作等;
从设备驱动的角度看,regulator系统比较简单,
Machine提供Supply与Consumer的对应关系、单板相关的约束条件(device);
Regulator提供电源芯片的控制函数,如使能/去能、设置电压/电流等(driver);
Consumer调用Regulator相关函数控制电源的开关、调节(use);
即一个描述关系,一个提供相关函数,一个调用相关函数。
regulator系统仍然是采用***总线设备驱动模型***。
device采用c文件或设备树的形式,提供硬件相关信息;
driver加载后,一但和device名字匹配,就调用probe()
函数注册register
,并绑定操作函数;
后面将使用两种实现regulator驱动。
这两种方式的核心都是一样的,
device
先提供Supply与Consumer的对应关系、单板相关的约束条件;
driver
提供电源芯片的控制函数,如使能/去能、设置电压/电流等。
regulator_consumer_supply
,约束条件regulator_init_data
:#if 1
static struct regulator_consumer_supply tiny4412_regulator_supplies[] =
{
REGULATOR_SUPPLY(“VCC_LCD”, “11c00000.lcd_s702”),//consumer的电源引脚名称;consumer的名字
};
#else
static struct regulator_consumer_supply tiny4412_regulator_supplies[] =
{
REGULATOR_SUPPLY(“VCC_LCD”, “lcd_s702”),
};
#endif
static struct regulator_init_data tiny4412_regulator_init_data =
{
.constraints = {
//.name = “tiny4412_regulator”,
.min_uV = 1000000,
.max_uV = 1000000,
.valid_modes_mask = REGULATOR_MODE_NORMAL,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
.boot_on = 0,
.always_on = 0,
},
.num_consumer_supplies = 1,
.consumer_supplies = tiny4412_regulator_supplies,
};
static void tiny4412_regulator_release(struct device *dev)
{
}
static struct platform_device tiny4412_regulator_dev =
{
.name = “tiny4412_regulator”,
.id = -1,
.dev = {
.release = tiny4412_regulator_release,
.platform_data = &tiny4412_regulator_init_data,
},
};
static int tiny4412_regulator_machine_init(void)
{
printk(“enter %s\n”, func);
platform_device_register(&tiny4412_regulator_dev);
return 0;
}
static void tiny4412_regulator_machine_exit(void)
{
printk(“enter %s\n”, func);
platform_device_unregister(&tiny4412_regulator_dev);
}
module_init(tiny4412_regulator_machine_init);
module_exit(tiny4412_regulator_machine_exit);
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng [email protected]”);
MODULE_DESCRIPTION(“Tiny4412 machine driver.”);
MODULE_ALIAS(“Exynos4412_machine”);
MODULE_VERSION(“V1.0”);
{% endcodeblock %}
regulator
:static int regulator_states = 0;
static int tiny4412_regulator_enable(struct regulator_dev *rdev)
{
printk(“enter %s\n”, func);
printk("------LCD Power Open.------\n");
regulator_states = 1;
return 0;
}
static int tiny4412_regulator_disable(struct regulator_dev *rdev)
{
printk(“enter %s\n”, func);
printk("------LCD Power Close.------\n");
regulator_states = 0;
return 0;
}
static int tiny4412_regulator_is_enabled(struct regulator_dev *rdev)
{
printk(“enter %s\n”, func);
printk("------LCD Power Test.------\n");
if (regulator_states)
return 1;
else
return 0;
}
static struct regulator_ops tiny4412_regulator_ops =
{
.enable = tiny4412_regulator_enable,
.disable = tiny4412_regulator_disable,
.is_enabled = tiny4412_regulator_is_enabled,
};
static struct regulator_desc tiny4412_regulator_desc =
{
.name = “tiny4412_regulator”,
.ops = &tiny4412_regulator_ops,
.type = REGULATOR_VOLTAGE,//电压源
.id = 0,
.owner = THIS_MODULE,
.n_voltages = 1,//能提供的电压数量
};
static struct regulator_dev *tiny4412_regulator_dev;
static int tiny4412_regulator_probe(struct platform_device *pdev)
{
struct regulator_config config = { };
config.dev = &pdev->dev;
config.init_data = dev_get_platdata(&pdev->dev);
printk("enter %s\n", __func__);
tiny4412_regulator_dev = devm_regulator_register(&pdev->dev, &tiny4412_regulator_desc, &config);
if (IS_ERR(tiny4412_regulator_dev))
{
printk("devm_regulator_register error!\n");
return PTR_ERR(tiny4412_regulator_dev);
}
return 0;
}
static int tiny4412_regulator_remove(struct platform_device *pdev)
{
printk(“enter %s\n”, func);
devm_regulator_unregister(&pdev->dev, tiny4412_regulator_dev);
return 0;
}
struct platform_driver tiny4412_regulator_drv =
{
.probe = tiny4412_regulator_probe,
.remove = tiny4412_regulator_remove,
.driver = {
.name = “tiny4412_regulator”,
}
};
static int tiny4412_regulator_init(void)
{
printk(“enter %s\n”, func);
platform_driver_register(&tiny4412_regulator_drv);
return 0;
}
static void tiny4412_regulator_exit(void)
{
printk(“enter %s\n”, func);
platform_driver_unregister(&tiny4412_regulator_drv);
}
module_init(tiny4412_regulator_init);
module_exit(tiny4412_regulator_exit);
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng [email protected]”);
MODULE_DESCRIPTION(“Tiny4412 regulator driver.”);
MODULE_ALIAS(“Exynos4412_regulator”);
MODULE_VERSION(“V1.0”);
{% endcodeblock %}
machine.ko
和regulator.ko
,名字匹配后调用probe()
注册regulator
。probe()
根据名字获取对应regulator
: //regulator
tiny4412_regulator = regulator_get(&pdev->dev, "VCC_LCD");
if (IS_ERR(tiny4412_regulator))
{
printk("regulator_get error!\n");
return -EIO;
}
再在suspend()
和resume()
封面便调用regulator_disable()
和regulator_enable()
。
此时,如果使用 系统睡眠模型 或 Runtime电源模型 进行休眠操作,就会调用到regulator系统的操作函数,实现电源管理芯片的关闭。
使用完后,释放regulator
:
static int lcd_remove(struct platform_device *pdev)
{
//Direct Off: ENVID and ENVID_F are set to "0" simultaneously.
unsigned int temp;
temp = readl(lcd_regs_base + VIDCON0);
temp &= ~(0x01 << 1 | 0x01 << 0);
writel(temp, lcd_regs_base + VIDCON0);
regulator_put(tiny4412_regulator);
pm_runtime_disable(&pdev->dev);
unregister_framebuffer(tiny4412_lcd);
dma_free_writecombine(NULL, tiny4412_lcd->fix.smem_len, tiny4412_lcd->screen_base, tiny4412_lcd->fix.smem_start);
framebuffer_release(tiny4412_lcd);
return 0;
}
与前面的操作几乎一样,只不过是在dts实现device
。
regulators
节点下添加新的regulator
,设置约束条件: regulators {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
mmc_reg: regulator@0{
compatible = "regulator-fixed";
reg = <0>;
regulator-name = "VMEM_VDD_2.8V";
regulator-min-microvolt = <2800000>;
regulator-max-microvolt = <2800000>;
};
lcd_reg: regulator@1{
compatible = "tiny4412,lcd_regulator";
regulator-name = "VCC_LCD";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
};
};
在lcd节点下,添加级联关系:
lcd_s702@11C00000 {
compatible = "tiny4412, lcd_s702";
reg = <0x11C00000 0x20c0 0x10010210 0x08 0x10023c80 0x04 0x1003c000 0x1000>;
pinctrl-names = "default";
pinctrl-0 = <&lcd_s702>;
clocks = <&clock CLK_FIMD0 &clock CLK_ACLK160>;
clock-names = "fimd0","aclk160";
vlcd-supply = <&lcd_reg>;
status = "okay";
};
其中vlcd-supply
与前面的regulator
联系了起来。
static int regulator_states = 0;
static int tiny4412_regulator_enable(struct regulator_dev *rdev)
{
printk(“enter %s\n”, func);
printk("------LCD Power Open.------\n");
regulator_states = 1;
return 0;
}
static int tiny4412_regulator_disable(struct regulator_dev *rdev)
{
printk(“enter %s\n”, func);
printk("------LCD Power Close.------\n");
regulator_states = 0;
return 0;
}
static int tiny4412_regulator_is_enabled(struct regulator_dev *rdev)
{
printk(“enter %s\n”, func);
printk("------LCD Power Test.------\n");
if (regulator_states)
return 1;
else
return 0;
}
static struct regulator_ops tiny4412_regulator_ops =
{
.enable = tiny4412_regulator_enable,
.disable = tiny4412_regulator_disable,
.is_enabled = tiny4412_regulator_is_enabled,
};
static struct regulator_desc tiny4412_regulator_desc =
{
.name = “tiny4412_regulator_dev”,
.ops = &tiny4412_regulator_ops,
.type = REGULATOR_VOLTAGE,//电压源
.id = 0,
.owner = THIS_MODULE,
.n_voltages = 1,//能提供的电压数量
};
static struct regulator_dev *tiny4412_regulator_dev;
static int tiny4412_regulator_probe(struct platform_device *pdev)
{
struct regulator_config config = { };
config.dev = &pdev->dev;
config.init_data = dev_get_platdata(&pdev->dev);
printk("enter %s\n", __func__);
tiny4412_regulator_dev = devm_regulator_register(&pdev->dev, &tiny4412_regulator_desc, &config);
if (IS_ERR(tiny4412_regulator_dev))
{
printk("devm_regulator_register error!\n");
return PTR_ERR(tiny4412_regulator_dev);
}
return 0;
}
static int tiny4412_regulator_remove(struct platform_device *pdev)
{
printk(“enter %s\n”, func);
devm_regulator_unregister(&pdev->dev, tiny4412_regulator_dev);
return 0;
}
static const struct of_device_id regulators_of_match[] =
{
{ .compatible = “tiny4412,lcd_regulator” },
{ },
};
MODULE_DEVICE_TABLE(of, regulators_of_match);
struct platform_driver tiny4412_regulator_drv =
{
.probe = tiny4412_regulator_probe,
.remove = tiny4412_regulator_remove,
.driver = {
.name = “tiny4412_regulator_drv”,
.of_match_table = of_match_ptr(regulators_of_match),
}
};
static int tiny4412_regulator_init(void)
{
printk(“enter %s\n”, func);
platform_driver_register(&tiny4412_regulator_drv);
return 0;
}
static void tiny4412_regulator_exit(void)
{
printk(“enter %s\n”, func);
platform_driver_unregister(&tiny4412_regulator_drv);
}
module_init(tiny4412_regulator_init);
module_exit(tiny4412_regulator_exit);
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“hceng [email protected]”);
MODULE_DESCRIPTION(“Tiny4412 regulator driver.”);
MODULE_ALIAS(“Exynos4412_regulator”);
MODULE_VERSION(“V1.0”);
{% endcodeblock %}
参考资料:
韦东山第三期项目视频_电源管理
蜗窝科技