1、MFD功能介绍
MFD(Multi-function Device)多功能设备,许多有共性的设备的集合,MFD由核心层(core)以及其下的“子设备”组成。从下文将会看到,MFD只是将设备注册到platform总线--因此,其子设备属于platform设备。它并没有对涉及到的设备或者驱动做实质性改变。但是,因为某些设备的共性,所以可以在MFD中提供共同的函数给其下子设备进行调用。
2、为什么会出现MFD子系统,以及有哪些多功能设备?
由于出现了一类具有多种功能的外围或CPU内部集成的硬件模块。
多功能设备有:
- PMIC,电源管理芯片
- DA9063:调节器,LED控制器,看门狗,实时时钟控制器,温度传感器,震动马达驱动,长按关机功能(ON key)
- MAX77843:调节器,充电器,燃油量表,触觉反馈,LED控制器,micro USB接口控制器
- Diolan DLN2:USB转i2c,SPI和GPIO控制器
3、MFD子系统的优点
- 允许在多个子系统中注册相同的驱动
- 允许驱动重用,多个多功能设备能重用其它子系统中的驱动
4、MFD提供的API
以下接口定义在include/linux/mfd/core.h中,在drivers/mfd/mfd-core.c中被实现:
extern 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);
5、tps6507x器件案例分析
5.1 tps6507x器件的驱动代码相关的路径如下:
include/linux/mfd/tps6507x.h
include/linux/input/tps6507x-ts.h
drivers/mfd/tps6507x.c
drivers/regulator/tps6507x-regulator.c
drivers/input/touchscreen/tps6507x-ts.c
5.2 tps6507x结构体
- 从该结构体可以看出,tps6507x系列芯片提供两种功能:电源管理功能(regulator)+触摸屏功能(touchscreen)
static const struct mfd_cell tps6507x_devs[] = {
{
.name = "tps6507x-pmic",
},
{
.name = "tps6507x-ts",
},
};
- tps6507x的读写接口是放在以下的结构体中,也就是所谓的共性
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;
};
5.3 驱动代码分析
- tps6507x.c驱动文件分析
subsys_initcall(tps6507x_i2c_init) => i2c_add_driver(&tps6507x_i2c_driver) => tps6507x_i2c_probe
static int tps6507x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct tps6507x_dev *tps6507x;
tps6507x = devm_kzalloc(&i2c->dev, sizeof(struct tps6507x_dev),
GFP_KERNEL);
if (tps6507x == NULL)
return -ENOMEM;
// 该函数调用dev_set_drvdata()函数绑定tps6507x结构体
i2c_set_clientdata(i2c, tps6507x);
tps6507x->dev = &i2c->dev;
tps6507x->i2c_client = i2c;
// 初始化读写函数
tps6507x->read_dev = tps6507x_i2c_read_device;
tps6507x->write_dev = tps6507x_i2c_write_device;
return devm_mfd_add_devices(tps6507x->dev, -1, tps6507x_devs,
ARRAY_SIZE(tps6507x_devs), NULL, 0, NULL);
}
- tps6507x的电压调节器驱动(tps6507x-regulator.c)
subsys_initcall(tps6507x_pmic_init) => platform_driver_register(&tps6507x_pmic_driver) => tps6507x_pmic_probe
static int tps6507x_pmic_probe(struct platform_device *pdev)
{
// 获取在mfd中通过i2c_set_clientdata设置的数据
struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
struct tps_info *info = &tps6507x_pmic_regs[0];
struct regulator_config config = { };
struct regulator_init_data *init_data;
struct regulator_dev *rdev;
struct tps6507x_pmic *tps;
struct tps6507x_board *tps_board;
struct of_regulator_match *tps6507x_reg_matches = NULL;
int i;
int error;
unsigned int prop;
/**
* tps_board points to pmic related constants
* coming from the board-evm file.
*/
tps_board = dev_get_platdata(tps6507x_dev->dev);
if (IS_ENABLED(CONFIG_OF) && !tps_board &&
tps6507x_dev->dev->of_node)
tps_board = tps6507x_parse_dt_reg_data(pdev,
&tps6507x_reg_matches);
if (!tps_board)
return -EINVAL;
/**
* init_data points to array of regulator_init structures
* coming from the board-evm file.
*/
init_data = tps_board->tps6507x_pmic_init_data;
if (!init_data)
return -EINVAL;
tps = devm_kzalloc(&pdev->dev, sizeof(*tps), GFP_KERNEL);
if (!tps)
return -ENOMEM;
mutex_init(&tps->io_lock);
/* common for all regulators */
tps->mfd = tps6507x_dev;
for (i = 0; i < TPS6507X_NUM_REGULATOR; i++, info++, init_data++) {
/* Register the regulators */
tps->info[i] = info;
if (init_data->driver_data) {
struct tps6507x_reg_platform_data *data =
init_data->driver_data;
tps->info[i]->defdcdc_default = data->defdcdc_default;
}
tps->desc[i].name = info->name;
tps->desc[i].id = i;
tps->desc[i].n_voltages = info->table_len;
tps->desc[i].volt_table = info->table;
// 设置regulator的操作函数
tps->desc[i].ops = &tps6507x_pmic_ops;
tps->desc[i].type = REGULATOR_VOLTAGE;
tps->desc[i].owner = THIS_MODULE;
config.dev = tps6507x_dev->dev;
config.init_data = init_data;
config.driver_data = tps;
if (tps6507x_reg_matches) {
error = of_property_read_u32(
tps6507x_reg_matches[i].of_node,
"ti,defdcdc_default", &prop);
if (!error)
tps->info[i]->defdcdc_default = prop;
config.of_node = tps6507x_reg_matches[i].of_node;
}
rdev = devm_regulator_register(&pdev->dev, &tps->desc[i],
&config);
if (IS_ERR(rdev)) {
dev_err(tps6507x_dev->dev,
"failed to register %s regulator\n",
pdev->name);
return PTR_ERR(rdev);
}
/* Save regulator for cleanup */
tps->rdev[i] = rdev;
}
tps6507x_dev->pmic = tps;
// 该函数设置的数据不清楚在哪里被调用,在当前文件中没有调用platform_get_drvdata的地方
platform_set_drvdata(pdev, tps6507x_dev);
return 0;
}
// regulator的操作函数定义
static struct regulator_ops tps6507x_pmic_ops = {
.is_enabled = tps6507x_pmic_is_enabled,
.enable = tps6507x_pmic_enable,
.disable = tps6507x_pmic_disable,
.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,
};
- tps6507x触摸屏驱动(tps6507x-ts.c)
获取MFD中使用i2c_set_clientdata绑定的tps6507x结构体;
填充tps6507x_ts结构体,注册函数tps6507x_ts_poll函数。
static int tps6507x_ts_probe(struct platform_device *pdev)
{
struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent);
const struct tps6507x_board *tps_board;
const struct touchscreen_init_data *init_data;
struct tps6507x_ts *tsc;
struct input_polled_dev *poll_dev;
struct input_dev *input_dev;
int error;
/*
* tps_board points to pmic related constants
* coming from the board-evm file.
*/
tps_board = dev_get_platdata(tps6507x_dev->dev);
if (!tps_board) {
dev_err(tps6507x_dev->dev,
"Could not find tps6507x platform data\n");
return -ENODEV;
}
/*
* init_data points to array of regulator_init structures
* coming from the board-evm file.
*/
init_data = tps_board->tps6507x_ts_init_data;
tsc = devm_kzalloc(&pdev->dev, sizeof(struct tps6507x_ts), GFP_KERNEL);
if (!tsc) {
dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
return -ENOMEM;
}
tsc->mfd = tps6507x_dev;
tsc->dev = tps6507x_dev->dev;
tsc->min_pressure = init_data ?
init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE;
snprintf(tsc->phys, sizeof(tsc->phys),
"%s/input0", dev_name(tsc->dev));
poll_dev = devm_input_allocate_polled_device(&pdev->dev);
if (!poll_dev) {
dev_err(tsc->dev, "Failed to allocate polled input device.\n");
return -ENOMEM;
}
tsc->poll_dev = poll_dev;
poll_dev->private = tsc;
poll_dev->poll = tps6507x_ts_poll;
poll_dev->poll_interval = init_data ?
init_data->poll_period : TSC_DEFAULT_POLL_PERIOD;
input_dev = poll_dev->input;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0);
input_dev->name = "TPS6507x Touchscreen";
input_dev->phys = tsc->phys;
input_dev->dev.parent = tsc->dev;
input_dev->id.bustype = BUS_I2C;
if (init_data) {
input_dev->id.vendor = init_data->vendor;
input_dev->id.product = init_data->product;
input_dev->id.version = init_data->version;
}
error = tps6507x_adc_standby(tsc);
if (error)
return error;
// 注册poll设备,该函数需要研究下
error = input_register_polled_device(poll_dev);
if (error)
return error;
return 0;
}
参考文档
1、https://bootlin.com/pub/conferences/2015/elce/belloni-mfd-regmap-syscon/belloni-mfd-regmap-syscon.pdf
2、https://www.cnblogs.com/dakewei/p/10991941.html