举例说明:
- devm_regmap_init_mmio_clk
- regmap_update_bits(dsi->regmap, DSI_PHY_TST_CTRL1, PHY_TESTEN, 0);
- regmap_read(dsi->regmap, DSI_PHY_TST_CTRL1, &data);
- regmap_write(dsi->regmap, DSI_GEN_VCID, GEN_VCID_RX(vcid));
先逐个说明
#define devm_regmap_init_mmio_clk(dev, clk_id, regs, config) \
__regmap_lockdep_wrapper(__devm_regmap_init_mmio_clk, #config, \
dev, clk_id, regs, config)
struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
const char *clk_id,
void __iomem *regs,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap_mmio_context *ctx;
ctx = regmap_mmio_gen_context(dev, clk_id, regs, config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
return __devm_regmap_init(dev, ®map_mmio, ctx, config,
lock_key, lock_name);
}
先看regmap_mmio_gen_context,这个函数主要是完成struct regmap_mmio_context *ctx这个指针的赋值,例如寄存器地址和数据的位宽,步长设置。同时如果clk_id不为空时,还需要请求时钟,并prepare。
这里重点关注第二个函数
参数说明:
dev:最开始传入的设备指针;
bus:上一级传入的regmap_mmio的指针,包含寄存器读写的实现;
bus_context:上一级中regmap_mmio_gen_context返回的指针;
config:最初devm_regmap_init_mmio_clk传入的config指针;
lock_key和lock_name:均为NULL;
struct regmap *__devm_regmap_init(struct device *dev,
const struct regmap_bus *bus,
void *bus_context,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap **ptr, *regmap;
ptr = devres_alloc(devm_regmap_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
regmap = __regmap_init(dev, bus, bus_context, config,
lock_key, lock_name);
if (!IS_ERR(regmap)) {
*ptr = regmap;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return regmap;
}
直接看__regmap_init这个函数
__regmap_init
map = kzalloc(sizeof(*map), GFP_KERNEL);
map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
map->format.pad_bytes = config->pad_bits / 8;
map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
config->val_bits + config->pad_bits, 8);
map->reg_shift = config->pad_bits % 8;
if (config->reg_stride)
map->reg_stride = config->reg_stride;
else
map->reg_stride = 1;
以上通过最初devm_regmap_init_mmio_clk传入的config指针完成regmap格式的初始化;
map->dev = dev;
map->bus = bus;
map->bus_context = bus_context;
....
1.如果regmap_mmio指针为空,则使用config传入的读写指针作为map的寄存器读写指针
2.如果regmap_mmio提供的读写指针不完整,则使用默认的读写寄存器操作
3.如果regmap_mmio的读写指针完整,则map->reg_read被赋值为_regmap_bus_read;
map->reg_update_bits被赋值为bus->reg_update_bits
if (!bus) {
map->reg_read = config->reg_read;
map->reg_write = config->reg_write;
map->defer_caching = false;
goto skip_format_initialization;
} else if (!bus->read || !bus->write) {
map->reg_read = _regmap_bus_reg_read;
map->reg_write = _regmap_bus_reg_write;
map->defer_caching = false;
goto skip_format_initialization;
} else {
map->reg_read = _regmap_bus_read;
map->reg_update_bits = bus->reg_update_bits;
}
regmap_update_bits(struct regmap *map, unsigned int reg,unsigned int mask, unsigned int val)
_regmap_update_bits(map, reg, mask, val, NULL, false)
_regmap_read(map, reg, &orig); 先读取原始的寄存器的值到orig中
做一下运算,得到需要重新写入寄存器的值
tmp = orig & ~mask;
tmp |= val & mask;
再次写入寄存器中
_regmap_write(map, reg, tmp);
下面分解来看_regmap_read和_regmap_write
map->reg_read(context, reg, val);
在我们初始化的时候map->reg_read被初始化为了regmap_bus_read,原因在于regmap_mmio实现了read和write的函数指针。
_regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
map->format.format_reg(map->work_buf, reg, map->reg_shift);
map->bus->read(map->bus_context,map->work_buf,map->format.reg_bytes +map->format.pad_bytes,val, val_len);
下面的32对应的config->reg_bits+config->pad_bits%8;我们当前传入的寄存器bit位数为32同时没有偏移。
case 32:
switch (reg_endian) {
case REGMAP_ENDIAN_BIG:
map->format.format_reg = regmap_format_32_be;
break;
case REGMAP_ENDIAN_NATIVE:
map->format.format_reg = regmap_format_32_native;
break;
default:
goto err_map;
}
break;
这儿需要研究下的是format.format_reg这个函数指针的指向:原则如下:
1.如果devm_regmap_init_mmio_clk传入的config指针中含有reg_format_endian不为0,那么依据该值参照上面的switch返回对应的指针;
2.如果devm_regmap_init_mmio_clk中的config指针没有配置reg_format_endian这个指针,或者配置了该项,但是该项的值为0.那么依据bus也就是regmap_mmio中的reg_format_endian_default成员,如果该成员存在,且其值不为0,那么同样参照上面的switch中对于的分支即可;
3.如果1-2都不满足那么返回REGMAP_ENDIAN_BIG,map->format.format_reg 被赋值为regmap_format_32_be.
这个例子中bus也就是regmap_mmio中的reg_format_endian_default被设置为了REGMAP_ENDIAN_NATIVE,
因此map->format.format_reg = regmap_format_32_native;
static void regmap_format_32_native(void *buf, unsigned int val,
unsigned int shift)
{
*(u32 *)buf = val << shift;
}
上面的三个参数分析下:
buf: map->work_buf
val:reg
shift: map->reg_shift
map->work_buf是在regmap_init的时候通过kzalloc分配的内存,这块内存的大小为:
config->reg_bits+config->val_bits + config->pad_bits,之后再对齐到8字节。
当前这个workbuf为32+32总共,64个字节。
那么,workbuf的前32个字节存放的就是寄存器的地址(可能含有偏移)
_regmap_read最终调用的就是map->bus->read,即对应的就是
参数说明:
context:对应的是regmap_mmio_gen_context返回的指针;
reg:map->work_buf,其中的第一个字节含有寄存器的地址;
reg_size:32个bit也就是4字节 ==== 4
val:上级传入的,等待返回的寄存器值;
val_size: 寄存器值的位数32位;也就是4个字节 ==== 4
static int regmap_mmio_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct regmap_mmio_context *ctx = context;
unsigned int offset;
int ret;
regmap_mmio_regsize_check(reg_size);
如果时钟域不为空,那么需要在真正读的时候使能时钟
if (!IS_ERR(ctx->clk)) {
ret = clk_enable(ctx->clk);
if (ret < 0)
return ret;
}
先取出偏移量,针对当前的场景,寄存器的位数是32,那么就取出map->work_buf的前面32个字节也就是传入的
寄存器地址(可能含有偏移量),将其作为读取的偏移
offset = regmap_mmio_get_offset(reg, reg_size);
while (val_size) {
switch (ctx->val_bytes) {
case 1:
*(u8 *)val = readb(ctx->regs + offset);
break;
case 2:
*(u16 *)val = readw(ctx->regs + offset);
break;
case 4:
寄存器中的数据是32位的,因此读取内存中的具体值,存放到val中
*(u32 *)val = readl(ctx->regs + offset);
break;
#ifdef CONFIG_64BIT
case 8:
*(u64 *)val = readq(ctx->regs + offset);
break;
#endif
default:
/* Should be caught by regmap_mmio_check_config */
BUG();
}
针对32bit也就是4个字节的情况,读取一次后也就结束退出了.
val_size -= ctx->val_bytes;
val += ctx->val_bytes;
offset += ctx->val_bytes;
}
if (!IS_ERR(ctx->clk))
clk_disable(ctx->clk);
return 0;
}
map->reg_write(context, reg, val);
这儿的reg_write指针指向的是哪儿呢?
我们留意到__regmap_init中
上面并没有实现map->reg_write,那在哪儿呢
reg_endian = regmap_get_reg_endian(bus, config);
val_endian = regmap_get_val_endian(dev, bus, config);
我们之前得到reg_endian为REGMAP_ENDIAN_NATIVE,同时val_endian的获取逻辑和之前的reg_endian基本是一致的,至少额外增加了在DTS中获取的判断:
if (of_property_read_bool(np, "big-endian"))
endian = REGMAP_ENDIAN_BIG;
else if (of_property_read_bool(np, "little-endian"))
endian = REGMAP_ENDIAN_LITTLE;
如上,如果DTS中有如上的配置,则按照具体情况返回,当前的情况下DTS并没有配置
返回的是REGMAP_ENDIAN_NATIVE,如下:
static struct regmap_bus regmap_mmio = {
.fast_io = true,
.write = regmap_mmio_write,
.gather_write = regmap_mmio_gather_write,
.read = regmap_mmio_read,
.free_context = regmap_mmio_free_context,
.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
};
回到最初的问题map->reg_write指针的来源:
情况一:
说明: config->reg_bits + map->reg_shift之后简称为寄存器地址BIT数
config->val_bits:简称为寄存器值BIT数
a) 如果寄存器地址的BIT数为2,但是寄存器值的BIT数为6;
b) 如果寄存器地址的BIT数为4,但是寄存器值的BIT数为12;
c) 如果寄存器地址的BIT数为7,但是寄存器值的BIT数为9;
d) 如果寄存器地址的BIT数为10,但是寄存器值的BIT数为14;
结论一:
map->reg_write = _regmap_bus_formatted_write;
如果寄存器值的BIT数为8,16,24,32.则
map->reg_write = _regmap_bus_raw_write;
情况二:
switch (config->val_bits) {
.......
case 32:
switch (val_endian) {
case REGMAP_ENDIAN_BIG:
map->format.format_val = regmap_format_32_be;
map->format.parse_val = regmap_parse_32_be;
map->format.parse_inplace = regmap_parse_32_be_inplace;
break;
case REGMAP_ENDIAN_LITTLE:
map->format.format_val = regmap_format_32_le;
map->format.parse_val = regmap_parse_32_le;
map->format.parse_inplace = regmap_parse_32_le_inplace;
break;
case REGMAP_ENDIAN_NATIVE:
map->format.format_val = regmap_format_32_native;
map->format.parse_val = regmap_parse_32_native;
break;
default:
goto err_map;
}
break;
if (map->format.format_write) { //这条分支不满足
map->defer_caching = false;
map->reg_write = _regmap_bus_formatted_write;
} else if (map->format.format_val) { //满足分支条件
map->defer_caching = true;
map->reg_write = _regmap_bus_raw_write;
}
如上
得到当前情况的最终结论:
map->reg_write = _regmap_bus_raw_write;
static int _regmap_bus_raw_write(void *context, unsigned int reg,
unsigned int val)
{
struct regmap *map = context;
WARN_ON(!map->bus || !map->format.format_val);
map->format.format_val(map->work_buf + map->format.reg_bytes
+ map->format.pad_bytes, val, 0);
return _regmap_raw_write(map, reg,
map->work_buf +
map->format.reg_bytes +
map->format.pad_bytes,
map->format.val_bytes);
}
分2步走:
通过之前的代码,知道map->format.format_val指向的是regmap_format_32_native
static void regmap_format_32_native(void *buf, unsigned int val,
unsigned int shift)
{
*(u32 *)buf = val << shift;
}
因此map->format.format_val的意思就是把即将写入寄存器的值先写入map->work_buf的后32位BIT中。
再看最后一步_regmap_raw_write
int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len)
{
void *work_val = map->work_buf + map->format.reg_bytes + map->format.pad_bytes;
....
//先将寄存器的地址写入前32 BIT,至此map->work_buf的前32个BIT存放的是寄存器的地址偏移,后32个BIT存放的是即将写入该寄存器中的值。
map->format.format_reg(map->work_buf, reg, map->reg_shift);
if (val != work_val && val_len == map->format.val_bytes) {
memcpy(work_val, val, map->format.val_bytes);
val = work_val;
}
....
//对于我们当前的情况val和work_val是相等的,在regmap_range的情况下会不相等,这里先不做考虑
if (val == work_val)
ret = map->bus->write(map->bus_context, map->work_buf,
map->format.reg_bytes +
map->format.pad_bytes +
val_len);
else if (map->bus->gather_write)
ret = map->bus->gather_write(map->bus_context, map->work_buf,
map->format.reg_bytes +
map->format.pad_bytes,
val, val_len);
}
函数最终还是走向了regmap_mmio_write
/*
context: 对应的是regmap_mmio_gen_context返回的指针;
data: 寄存器地址偏移的指针地址;
count:地址长度+数据长度
*/
static int regmap_mmio_write(void *context, const void *data, size_t count)
{
struct regmap_mmio_context *ctx = context;
//这里的offset为: 4
unsigned int offset = ctx->reg_bytes + ctx->pad_bytes;
regmap_mmio_count_check(count, offset);
return regmap_mmio_gather_write(context, data, ctx->reg_bytes,
data + offset, count - offset);
}
函数走向regmap_mmio_gather_write
/*
参数说明:
context:对应的是regmap_mmio_gen_context返回的指针;
reg:对应的是寄存器的地址指针
reg_size:对应的是寄存器的地址大小32BIT ===== 4 (字节)
val:work_buf中即将写入寄存器地址中的数据指针的位置
val_size:需要写入寄存器地址中数据的长度 ===== 4(字节)
*/
static int regmap_mmio_gather_write(void *context,
const void *reg, size_t reg_size,
const void *val, size_t val_size)
{
struct regmap_mmio_context *ctx = context;
unsigned int offset;
int ret;
regmap_mmio_regsize_check(reg_size);
if (!IS_ERR(ctx->clk)) {
ret = clk_enable(ctx->clk);
if (ret < 0)
return ret;
}
//取出的offset对应的是寄存器地址的偏移,同读取的时候一致,也是由最开始_regmap_write传入的值
offset = regmap_mmio_get_offset(reg, reg_size);
while (val_size) {
switch (ctx->val_bytes) {
case 1:
writeb(*(u8 *)val, ctx->regs + offset);
break;
case 2:
writew(*(u16 *)val, ctx->regs + offset);
break;
case 4:
//将数值写入到寄存器中
writel(*(u32 *)val, ctx->regs + offset);
break;
#ifdef CONFIG_64BIT
case 8:
writeq(*(u64 *)val, ctx->regs + offset);
break;
#endif
default:
/* Should be caught by regmap_mmio_check_config */
BUG();
}
val_size -= ctx->val_bytes;
val += ctx->val_bytes;
offset += ctx->val_bytes;
}
if (!IS_ERR(ctx->clk))
clk_disable(ctx->clk);
return 0;
}
同时regmap_write以及regmap_read最终调用的也是_regmap_write和_regmap_read,如下:
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
if (reg % map->reg_stride)
return -EINVAL;
map->lock(map->lock_arg);
ret = _regmap_write(map, reg, val);
map->unlock(map->lock_arg);
return ret;
}
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
int ret;
if (reg % map->reg_stride)
return -EINVAL;
map->lock(map->lock_arg);
ret = _regmap_read(map, reg, val);
map->unlock(map->lock_arg);
return ret;
}
至此整个Regmap机制分析完成