1. 概念:
Regulator : 電源芯片, 比如電壓轉換芯片
Consumer : 消費者,使用電源的部件, Regulator是給Consumer供電的
machine : 單板,上面焊接有Regulator和Consumer
Constraints : 約束, 比如某個電源管理芯片輸出的電壓范圍
Supply : 提供電源的部件, Regulator就是一個Supply; Regulator A可以給Regulator B供電, 那么Regulator B的Supply就是A
2. 寫驅動程序:
(1). regulator驅動:
注冊一個platform_driver: 在它的probe函數里分配、設置、注冊一個regulator
"設置"里要做的事情: 實現regulator的操作, 比如enable, disable, set_voltage
(2). machine(單板)驅動:
注冊一個platform_device: 在它的私有數據里指定regulator和consume的對應關系(這個電源芯片給哪一個部件供電)
指定約束條件(比如電壓范圍)
(3). consumer使用此電源的設備驅動:
使用即可: regulator_get, regulator_enable, regulator_disable, regulator_set_voltage....
注: 這只是沒有使用設備樹的情況下是這樣的,其實就是以平台設備模型注冊一個regulator,然后consumer去使用。
3. 不使用設備樹的regulator_register流程分析:
// 分配regulator_dev rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); /* set regulator constraints */ set_machine_constraints add_regulator_attributes /* add consumers devices */ set_consumer_device_supply 在regulator_map_list鏈表里生成一項regulator_map: 它里面有dev_name(consumer的名字),supply(cosumer的電源引腳名字) // 把regulator_dev放入regulator_list list_add(&rdev->list, ®ulator_list);
4. 使用設備樹的regulator_register()和regulator_get()流程分析:
regulator_register(const struct regulator_desc *, const struct regulator_config *) //drivers/regulator/core.c 先自行各種檢查,然后構造一個struct regulator_dev,從設備樹中獲取它的各種屬性 regulator_of_get_init_data //drivers/regulator/of_regulator.c of_get_regulation_constraints //drivers/regulator/of_regulator.c 通過設備樹構造init_data,和約束 若設備樹中指定了“regulator-initial-mode”且regulator_desc.of_map_mode()存在,則調用它 初始化rdev->consumer_list,rdev->list,rdev->notifier,指定rdev->disable_work=regulator_disable_work 如果init_data->regulator_init()存在則調用 判斷是不是gpio控制的regulator,如果是就調用regulator_ena_gpio_request() 使rdev->dev.class = ®ulator_class,此時sysfs下會有"regulator"文件夾,其下有regulator_dev_attrs中列出的文件。 自動遞增設置&rdev->dev的name為"regulator.%lu" "regulator"下的"regulator.%lu"下才會有regulator_dev_attrs中列出的文件了。 set_machine_constraints device_register(&rdev->dev) //注冊到設備模型 dev_set_drvdata(&rdev->dev, rdev); rdev_init_debugfs(rdev);
使用設備樹的regulator_get()流程分析: struct regulator *regulator_get(struct device *dev, const char *id) //drivers/regulator/core.c, arg2為consumer的電源引腳 regulator_dev_lookup //drivers/regulator/core.c 使用devname與全局鏈表regulator_map_list中的每一項struct regulator_map->dev_name進行匹配,並返回匹配的struct regulator_dev結構體。 regulator_resolve_supply 還要和rdev->supply_name的名字逐個比較 create_regulator 根據struct regulator_dev創建一個struct regulator並返回
5.驅動示例代碼
/* 文件名:machine.c 作為一個regulator平台設備模型的設備端。 * 參考: arch\arm\mach-omap2\board-2430sdp.c * 這里只是注冊一個單板的所有regulator的平台設備端,目前這類文件應該被設備樹給替代了 */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/err.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/mfd/core.h> /* 分配/設置/注冊regulator_init_data */ #if 0 regulator_consumer_supply: const char *dev_name; /* consumer的名字 */ const char *supply; /* consumer的電源引腳名稱 */ #endif static struct regulator_consumer_supply myregulator_supplies[] = { REGULATOR_SUPPLY("VCC", "mylcd"), /*此regulator是對設備"mylcd"上的"VCC"引腳供電的*/ }; static struct regulator_init_data myregulator_init_data = { .constraints = { .min_uV = 12000000, .max_uV = 12000000, .valid_modes_mask = REGULATOR_MODE_NORMAL, .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, .num_consumer_supplies = ARRAY_SIZE(myregulator_supplies), .consumer_supplies = myregulator_supplies, }; static void myregulator_release(struct device * dev) { } static struct platform_device myregulator_dev = { .name = "myregulator", /* 平台設備設備端名字*/ .id = -1, .dev = { .release = myregulator_release, .platform_data = &myregulator_init_data, }, }; /*使用設備樹后就不再需要注冊平台設備端了*/ static int myregulator_machine_init(void) { platform_device_register(&myregulator_dev); return 0; } static void myregulator_machine_exit(void) { platform_device_unregister(&myregulator_dev); } module_init(myregulator_machine_init); module_exit(myregulator_machine_exit); MODULE_LICENSE("GPL v2");
/* 文件名:regulator.c 作為一個regulator平台設備模型的驅動端。 * 參考: drivers/regulator/tps6105x-regulator.c * 其實machine.c充當的是平台設備的設備端,將單板上的所有regulator寫在了一起,目前已被設備樹替代。 * regulator.c充當的是平台設備的驅動端,每個regulator都可能有一個平台設備驅動端。 */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/err.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/mfd/core.h> static volatile unsigned long *gpbcon; static volatile unsigned long *gpbdat; static int myregulator_enable(struct regulator_dev *rdev) { *gpbdat |= 1; /* 輸出高電平 */ return 0; } static int myregulator_disable(struct regulator_dev *rdev) { *gpbdat &= ~1; /* 輸出低電平 */ return 0; } static int myregulator_is_enabled(struct regulator_dev *rdev) { if (*gpbdat & 1) return 1; else return 0; } static struct regulator_ops myregulator_ops = { .enable = myregulator_enable, .disable = myregulator_disable, .is_enabled = myregulator_is_enabled, }; static struct regulator_desc myregulator_desc = { .name = "myregulator", .ops = &myregulator_ops, .type = REGULATOR_VOLTAGE, .id = 0, .owner = THIS_MODULE, .n_voltages = 1, /*只提供一種電壓值得*/ }; static struct regulator_dev *myregulator_dev; static int myregulator_probe(struct platform_device *pdev) { struct regulator_init_data *init_data = dev_get_platdata(&pdev->dev); /*可以在platform_driver初始化的時候對dev賦值*//* 分配/設置/注冊 regulator */ myregulator_dev = regulator_register(&myregulator_desc, &pdev->dev, init_data, NULL, NULL); if (IS_ERR(myregulator_dev)) { printk("regulator_register error!\n"); return -EIO; } return 0; } static int myregulator_remove(struct platform_device *pdev) { regulator_unregister(myregulator_dev); return 0; } struct platform_driver myregulator_drv = { .probe = myregulator_probe, .remove = myregulator_remove, .driver = { .name = "myregulator", /* 驅動上面regulator平台設備的設備端 */ } }; static int myregulator_init(void) { platform_driver_register(&myregulator_drv); return 0; } static void myregulator_exit(void) { platform_driver_unregister(&myregulator_drv); } module_init(myregulator_init); module_exit(myregulator_exit); MODULE_LICENSE("GPL v2");
/* 文件名:consumer_lcd.c 作為一個consumer驅動 * */ static int mylcd_open(struct fb_info *info, int user) { pm_runtime_get_sync(&lcd_dev.dev); return 0; } static int mylcd_release(struct fb_info *info, int user) { pm_runtime_mark_last_busy(&lcd_dev.dev); pm_runtime_put_sync_autosuspend(&lcd_dev.dev); return 0; } 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; } } static struct notifier_block lcd_pm_notif_block = { .notifier_call = lcd_suspend_notifier, }; static void lcd_release(struct device * dev) { } static struct platform_device lcd_dev = { /* regulator匹配1 */ .name = "mylcd", .id = -1, .dev = { .release = lcd_release, }, }; static struct regulator *myregulator; static int lcd_probe(struct platform_device *pdev) { myregulator = regulator_get(pdev->dev, "VCC"); /* regulator 匹配2 */ if (IS_ERR(myregulator)) { printk("regulator_get error!\n"); return -EIO; } regulator_enable(myregulator); /*沒有它會報unblance regulator disable of myregulator*/ pm_runtime_set_active(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_enable(&pdev->dev); return 0; } static int lcd_remove(struct platform_device *pdev) { regulator_put(myregulator); pm_runtime_disable(&pdev->dev); return 0; } static int lcd_suspend(struct device *dev) { int i; unsigned long *dest = &lcd_regs_backup; unsigned long *src = lcd_regs; for (i = 0; i < sizeof(lcd_regs_backup)/sizeof(unsigned long); i++) { dest[i] = src[i]; } lcd_regs->lcdcon1 &= ~(1<<0); /* 關閉LCD本身 */ //*gpbdat &= ~1; /* 關閉背光 */ regulator_disable(myregulator); return 0; } static int lcd_resume(struct device *dev) { int i; unsigned long *dest = lcd_regs; unsigned long *src = &lcd_regs_backup; struct clk *clk = clk_get(NULL, "lcd"); clk_enable(clk); clk_put(clk); for (i = 0; i < sizeof(lcd_regs_backup)/sizeof(unsigned long); i++) { dest[i] = src[i]; } regulator_enable(myregulator); return 0; } static struct dev_pm_ops lcd_pm = { .suspend = lcd_suspend, .resume = lcd_resume, .runtime_suspend = lcd_suspend, .runtime_resume = lcd_resume, }; struct platform_driver lcd_drv = { .probe = lcd_probe, .remove = lcd_remove, .driver = { .name = "mylcd", .pm = &lcd_pm, } }; static int lcd_init(void) { /* 電源管理 */ register_pm_notifier(&lcd_pm_notif_block); platform_device_register(&lcd_dev); platform_driver_register(&lcd_drv); return 0; } static void lcd_exit(void) { unregister_pm_notifier(&lcd_pm_notif_block); platform_device_unregister(&lcd_dev); platform_driver_unregister(&lcd_drv); } module_init(lcd_init); module_exit(lcd_exit); MODULE_LICENSE("GPL");
