分析用的內核版本爲5.1.3linux
1.MFD全稱數組
Multi-function Device,多功能設備app
2. 爲什麼會出現MFD子系統dom
因爲出現了一類具備多種功能的外圍設備或cpu內部集成的硬件模塊函數
3. 有哪些多功能設備呢?this
3.1 PMIC,電源管理芯片atom
da9063: 調節器,led控制器,看門狗,實時時鐘控制器,溫度傳感器,震動馬達驅動,長按關機功能(ON key)spa
max77843: 調節器,充電器,燃油量表,觸覺反饋,led控制器,micro USB接口控制器code
wm831x: 調節器,時鐘,實時時鐘控制器,看門狗,觸摸控制器,溫度傳感器,背光控制器,狀態led控制器,GPIO,長按關機功能(ON key),ADCorm
其它: 甚至具備codec功能
3.2 atmel-hlcdc: 顯示控制器和背光pwm
3.3 Diolan DLN2: USB轉I2C,SPI和GPIO控制器
3.4 Realtek PCI-E讀卡器: SD/MMC和記憶棒讀取器
4. MFD子系統解決的主要問題
在不一樣的內核子系統中註冊這些驅動。特別是外部外圍設備僅僅由一個結構體struct device(或是指定的i2c_client或spi_device)呈現
5. MFD子系統的優勢有哪些?
5.1 容許在多個子系統中註冊相同的設備
5.2 MFD驅動必須可否複用總線(主要是關於鎖的處理)和處理中斷請求
5.3 處理時鐘
5.4 須要配置IP
5.5 容許驅動重用,多個多功能設備能重用其它子系統中的驅動
6. MFD提供的API
int mfd_add_devices(struct device *parent,int id, const struct mfd_cell *cells, int n_devs,
struct resource *mem_base, int irq_base, struct irq_domain *irq_domain);
extern void mfd_remove_devices(struct device *parent);
這些接口定義在include/linux/mfd/core.h中,在drivers/mfd/mtd-core.c中被實現
7. MFD提供的結構體
struct mfd_cell { const char *name; int id; /* refcounting for multiple drivers to use a single cell */ atomic_t *usage_count; int (*enable)(struct platform_device *dev); int (*disable)(struct platform_device *dev); int (*suspend)(struct platform_device *dev); int (*resume)(struct platform_device *dev); /* platform data passed to the sub devices drivers */ void *platform_data; size_t pdata_size; /* device properties passed to the sub devices drivers */ struct property_entry *properties; /* * Device Tree compatible string * See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details */ const char *of_compatible; /* Matches ACPI */ const struct mfd_cell_acpi_match *acpi_match; /* * These resources can be specified relative to the parent device. * For accessing hardware you should use resources from the platform dev */ int num_resources; const struct resource *resources; /* don't check for resource conflicts */ bool ignore_resource_conflicts; /* * Disable runtime PM callbacks for this subdevice - see * pm_runtime_no_callbacks(). */ bool pm_runtime_no_callbacks; /* A list of regulator supplies that should be mapped to the MFD * device rather than the child device when requested */ const char * const *parent_supplies; int num_parent_supplies; };
8. 示例分析
8.1 分析tps6507x的多功能驅動
8.1.1 涉及的文件
drivers/mfd/tps6507x.c
include/linux/mfd/tps6507x.h
drivers/regulator/tps6507x-regulator.c
drivers/input/touchscreen/tps6507x-ts.c
8.1.2 涉及的結構體
static const struct mfd_cell tps6507x_devs[] = { { .name = "tps6507x-pmic", }, { .name = "tps6507x-ts", }, };
從以上結構體能夠得出,tps6507x系列芯片提供兩種功能: 電源管理功能(regulator)+觸摸屏功能(touchscreen)
static struct i2c_driver tps6507x_i2c_driver = { .driver = { .name = "tps6507x", .of_match_table = of_match_ptr(tps6507x_of_match), }, .probe = tps6507x_i2c_probe, .id_table = tps6507x_i2c_id, };
這個結構體爲tps6507x提供探測函數tps6507x_i2c_probe
struct tps6507x_dev { struct device *dev; struct i2c_client *i2c_client; int (*read_dev)(struct tps6507x_dev *tps6507x, char reg, int size, void *dest); int (*write_dev)(struct tps6507x_dev *tps6507x, char reg, int size, void *src); /* Client devices */ struct tps6507x_pmic *pmic; };
tps6507x 的讀寫接口就是放在這個結構體中,這也就是所謂的共性
8.1.3 對tps6507x進行初始化
subsys_initcall(tps6507x_i2c_init);
調用路徑以下:
tps6507x_i2c_init->i2c_add_driver
8.1.4 探測函數tps6507x_i2c_probe作了些什麼?
註冊tps6507x的讀寫函數: tps6507x_i2c_read_device和tps6507x_i2c_write_device到結構體struct tps6507x_dev中
8.1.5 tps6507x的兩種功能實如今哪裏呢?
drivers/regulator/tps6507x-regulator.c,這裏面實現電源管理功能(電壓調節器驅動)
drivers/input/touchscreen/tps6507x-ts.c,這裏面實現觸摸屏功能
8.1.6 tps6507x電壓調節器驅動
8.1.6.1 調用路徑
subsys_initcall(tps6507x_pmic_init);
tps6507x_pmic_init->platform_driver_register
8.1.6.2 探測函數tps6507x_pmic_probe幹了些什麼?
獲取共用的結構體struct tps6507x_dev
再註冊相關的結構體以便提供pmic的相關操做接口,以下:
static struct regulator_ops tps6507x_pmic_ops = { .is_enabled = tps6507x_pmic_is_enabled, 檢查tps6507x的pmic功能是否已經使能了 .enable = tps6507x_pmic_enable, 使能tps6507x的pmic功能 .disable = tps6507x_pmic_disable, 禁用tsp6507x的pmic功能 .get_voltage_sel = tps6507x_pmic_get_voltage_sel, 獲取電壓值 .set_voltage_sel = tps6507x_pmic_set_voltage_sel, 設置電壓值 .list_voltage = regulator_list_voltage_table, 列出電壓表 .map_voltage = regulator_map_voltage_ascend, };
8.1.7 tps6507x觸摸屏驅動
8.1.7.1 驅動在哪裏?
drivers/input/touchscreen/tps6507x-ts.c
8.1.7.2 分析probe函數都作了些什麼?
獲取公用的結構體struct tps6507x_dev
填充結構體struct tps6507x_ts,關鍵是註冊了函數tps6507x_ts_poll
8.2 分析da9063相關驅動
8.2.1 mfd驅動
8.2.1.1 相關源碼
drivers/mfd/da9063-i2c.c
8.2.1.2 分析探測函數da9063_i2c_probe的調用路徑
da9063_i2c_probe->da9063_device_init
8.2.1.3 da9063_device_init作了些什麼?
讀取da9063的芯片ID,檢查是否匹配
讀取da9063的variant ID,不一樣的variant ID表示不一樣的封裝
經過接口devm_mfd_add_devices添加具體的結構體struct mfd_cell數組,這個數組裏包含了多個驅動相關的信息,如名字,資源等
8.2.1.4 結構體數組da906_common_devs
static const struct mfd_cell da9063_common_devs[] = { { .name = DA9063_DRVNAME_REGULATORS, .num_resources = ARRAY_SIZE(da9063_regulators_resources), .resources = da9063_regulators_resources, }, { .name = DA9063_DRVNAME_LEDS, }, { .name = DA9063_DRVNAME_WATCHDOG, .of_compatible = "dlg,da9063-watchdog", }, { .name = DA9063_DRVNAME_HWMON, .num_resources = ARRAY_SIZE(da9063_hwmon_resources), .resources = da9063_hwmon_resources, }, { .name = DA9063_DRVNAME_ONKEY, .num_resources = ARRAY_SIZE(da9063_onkey_resources), .resources = da9063_onkey_resources, .of_compatible = "dlg,da9063-onkey", }, { .name = DA9063_DRVNAME_VIBRATION, }, };
這個結構體數組中就包含了調節器,led控制器,看門狗,硬件監測(電壓監測,溫度監測),長按關鍵功能(onkey),震動等驅動名稱,也就是da9063會關聯(具備)這些功能,da9063有兩種硬件版本,一種爲DA9063,另外一種爲DA9063L,這兩種硬件的差別在於DA9063具備實時時鐘功能,然後者沒有此功能
8.2.1.5 結構體數組da9063_devs
/* Only present on DA9063 , not on DA9063L */ static const struct mfd_cell da9063_devs[] = { { .name = DA9063_DRVNAME_RTC, .num_resources = ARRAY_SIZE(da9063_rtc_resources), .resources = da9063_rtc_resources, .of_compatible = "dlg,da9063-rtc", }, };
8.2.1.6 da9063_common_devs和da9063_devs中的這些具體的驅動實如今哪裏?
drivers/regulator/da9063-regulator.c (調節器驅動)
(沒有找到da9063的led控制器驅動)
drivers/watchdog/da9063_wdt.c (看門狗驅動)
(沒有找到da9063的硬件監測驅動)
drivers/input/misc/da9063_onkey.c (onkey驅動)
(沒有找到da9063的震動功能驅動)
drivers/rtc/rtc-da9063.c (實時時鐘驅動)
8.2.1.7 重要的結構體struct da9063
struct da9063 { /* Device */ struct device *dev; enum da9063_type type; unsigned char variant_code; unsigned int flags; /* Control interface */ struct regmap *regmap; /* Interrupts */ int chip_irq; unsigned int irq_base; struct regmap_irq_chip_data *regmap_irq; };
參考資料: