Linux 核心裝置驅動程式通過 devm_regmap_init_mmio()
等函數獲得 struct regmap
結構物件,該物件包含可用於存取裝置暫存器的全部資訊,包括定義存取操作如何執行的 bus,定義了各個裝置暫存器的讀寫屬性的 config,以及加速裝置暫存器存取的 cache。
Linux 核心裝置驅動程式可以通過 regmap_write()
、regmap_read()
和 regmap_update_bits()
等函數讀寫裝置暫存器,通過 regcache_sync()
、regcache_cache_only()
、regcache_cache_bypass()
和 regcache_mark_dirty()
等函數操作快取。
通過 I2C 存取的裝置暫存器,可以使用 regmap 機制來存取。如在 ALC 5651 audio codec 核心裝置驅動程式裡,在 probe
操作中建立 regmap 物件 (位於 sound/soc/codecs/rt5651.c
):
static const struct regmap_config rt5651_regmap = {
.reg_bits = 8,
.val_bits = 16,
.max_register = RT5651_DEVICE_ID + 1 + (ARRAY_SIZE(rt5651_ranges) *
RT5651_PR_SPACING),
.volatile_reg = rt5651_volatile_register,
.readable_reg = rt5651_readable_register,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = rt5651_reg,
.num_reg_defaults = ARRAY_SIZE(rt5651_reg),
.ranges = rt5651_ranges,
.num_ranges = ARRAY_SIZE(rt5651_ranges),
.use_single_read = true,
.use_single_write = true,
};
. . . . . .
static int rt5651_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct rt5651_priv *rt5651;
int ret;
int err;
rt5651 = devm_kzalloc(&i2c->dev, sizeof(*rt5651),
GFP_KERNEL);
if (NULL == rt5651)
return -ENOMEM;
i2c_set_clientdata(i2c, rt5651);
rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap);
if (IS_ERR(rt5651->regmap)) {
ret = PTR_ERR(rt5651->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
. . . . . .
之後對裝置暫存器的存取方法,同 mmio 的一樣。這裡建立 regmap 物件的方法為呼叫 devm_regmap_init_i2c()
,這是一個宏,其定義 (位於 include/linux/regmap.h
) 如下:
struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name);
. . . . . .
/**
* devm_regmap_init_i2c() - Initialise managed register map
*
* @i2c: Device that will be interacted with
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer
* to a struct regmap. The regmap will be automatically freed by the
* device management code.
*/
#define devm_regmap_init_i2c(i2c, config) \
__regmap_lockdep_wrapper
這個宏呼叫了 __devm_regmap_init_i2c()
函數,該函數定義 (位於 drivers/base/regmap/regmap-i2c.c
) 如下:
static const struct regmap_bus regmap_smbus_byte = {
.reg_write = regmap_smbus_byte_reg_write,
.reg_read = regmap_smbus_byte_reg_read,
};
. . . . . .
static const struct regmap_bus regmap_smbus_word = {
.reg_write = regmap_smbus_word_reg_write,
.reg_read = regmap_smbus_word_reg_read,
};
. . . . . .
static const struct regmap_bus regmap_smbus_word_swapped = {
.reg_write = regmap_smbus_word_write_swapped,
.reg_read = regmap_smbus_word_read_swapped,
};
. . . . . .
static const struct regmap_bus regmap_i2c = {
.write = regmap_i2c_write,
.gather_write = regmap_i2c_gather_write,
.read = regmap_i2c_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
. . . . . .
static const struct regmap_bus regmap_i2c_smbus_i2c_block = {
.write = regmap_i2c_smbus_i2c_write,
.read = regmap_i2c_smbus_i2c_read,
.max_raw_read = I2C_SMBUS_BLOCK_MAX,
.max_raw_write = I2C_SMBUS_BLOCK_MAX,
};
. . . . . .
static const struct regmap_bus regmap_i2c_smbus_i2c_block_reg16 = {
.write = regmap_i2c_smbus_i2c_write_reg16,
.read = regmap_i2c_smbus_i2c_read_reg16,
.max_raw_read = I2C_SMBUS_BLOCK_MAX,
.max_raw_write = I2C_SMBUS_BLOCK_MAX,
};
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
const struct regmap_config *config)
{
const struct i2c_adapter_quirks *quirks;
const struct regmap_bus *bus = NULL;
struct regmap_bus *ret_bus;
u16 max_read = 0, max_write = 0;
if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
bus = ®map_i2c;
else if (config->val_bits == 8 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
bus = ®map_i2c_smbus_i2c_block;
else if (config->val_bits == 8 && config->reg_bits == 16 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
bus = ®map_i2c_smbus_i2c_block_reg16;
else if (config->val_bits == 16 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_WORD_DATA))
switch (regmap_get_val_endian(&i2c->dev, NULL, config)) {
case REGMAP_ENDIAN_LITTLE:
bus = ®map_smbus_word;
break;
case REGMAP_ENDIAN_BIG:
bus = ®map_smbus_word_swapped;
break;
default: /* everything else is not supported */
break;
}
else if (config->val_bits == 8 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_BYTE_DATA))
bus = ®map_smbus_byte;
if (!bus)
return ERR_PTR(-ENOTSUPP);
quirks = i2c->adapter->quirks;
if (quirks) {
if (quirks->max_read_len &&
(bus->max_raw_read == 0 || bus->max_raw_read > quirks->max_read_len))
max_read = quirks->max_read_len;
if (quirks->max_write_len &&
(bus->max_raw_write == 0 || bus->max_raw_write > quirks->max_write_len))
max_write = quirks->max_write_len;
if (max_read || max_write) {
ret_bus = kmemdup(bus, sizeof(*bus), GFP_KERNEL);
if (!ret_bus)
return ERR_PTR(-ENOMEM);
ret_bus->free_on_exit = true;
ret_bus->max_raw_read = max_read;
ret_bus->max_raw_write = max_write;
bus = ret_bus;
}
}
return bus;
}
. . . . . .
struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __devm_regmap_init(&i2c->dev, bus, &i2c->dev, config,
lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_i2c);
__devm_regmap_init_i2c()
函數首先根據暫存器對映的設定,如暫存器地址的位數,暫存器值的位數,I2C 匯流排的功能特性,大尾端還是小尾端等,選擇裝置暫存器的存取操作,即 struct regmap_bus
,然後如同 __devm_regmap_init_mmio_clk()
函數一樣,通過 __devm_regmap_init()
函數建立並初始化 regmap 物件。
這裡通過 i2c_check_functionality()
函數判斷 I2C 匯流排的功能特性,這個函數定義 (位於 include/linux/i2c.h
) 如下:
/* Return the functionality mask */
static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
{
return adap->algo->functionality(adap);
}
/* Return 1 if adapter supports everything we need, 0 if not. */
static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
{
return (func & i2c_get_functionality(adap)) == func;
}
即通過 I2C 匯流排介面卡驅動程式實現的 functionality
操作來判斷。ALC 5651 Linux 核心驅動程式的暫存器對映設定,暫存器地址為 8 位,值為 16 位,對於標準的 I2C 匯流排,對應的 I2C IO 操作如下:
static int regmap_i2c_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret;
ret = i2c_master_send(i2c, data, count);
if (ret == count)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static int regmap_i2c_gather_write(void *context,
const void *reg, size_t reg_size,
const void *val, size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct i2c_msg xfer[2];
int ret;
/* If the I2C controller can't do a gather tell the core, it
* will substitute in a linear write for us.
*/
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_NOSTART))
return -ENOTSUPP;
xfer[0].addr = i2c->addr;
xfer[0].flags = 0;
xfer[0].len = reg_size;
xfer[0].buf = (void *)reg;
xfer[1].addr = i2c->addr;
xfer[1].flags = I2C_M_NOSTART;
xfer[1].len = val_size;
xfer[1].buf = (void *)val;
ret = i2c_transfer(i2c->adapter, xfer, 2);
if (ret == 2)
return 0;
if (ret < 0)
return ret;
else
return -EIO;
}
static int regmap_i2c_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct i2c_msg xfer[2];
int ret;
xfer[0].addr = i2c->addr;
xfer[0].flags = 0;
xfer[0].len = reg_size;
xfer[0].buf = (void *)reg;
xfer[1].addr = i2c->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = val_size;
xfer[1].buf = val;
ret = i2c_transfer(i2c->adapter, xfer, 2);
if (ret == 2)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static const struct regmap_bus regmap_i2c = {
.write = regmap_i2c_write,
.gather_write = regmap_i2c_gather_write,
.read = regmap_i2c_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
這裡構造訊息給 I2C 匯流排驅動程式,呼叫 Linux 核心 I2C 子系統提供的 i2c_master_send()
和 i2c_transfer()
等操作,完成對裝置暫存器的讀寫。Linux 核心 I2C 子系統及 I2C 匯流排驅動程式的更多細節這裡不多贅述。
Linux 核心裝置驅動程式通過 regmap_write()
等函數寫裝置暫存器,相關的這些函數原型 (位於 include/linux/regmap.h
) 如下:
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val);
int regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len);
int regmap_noinc_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len);
int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
size_t val_count);
int regmap_multi_reg_write(struct regmap *map, const struct reg_sequence *regs,
int num_regs);
int regmap_multi_reg_write_bypassed(struct regmap *map,
const struct reg_sequence *regs,
int num_regs);
int regmap_raw_write_async(struct regmap *map, unsigned int reg,
const void *val, size_t val_len);
regmap_write()
和 regmap_write_async()
函數分別同步和非同步地寫一個裝置暫存器,這兩個函數定義 (位於 drivers/base/regmap/regmap.c
) 如下:
bool regmap_reg_in_ranges(unsigned int reg,
const struct regmap_range *ranges,
unsigned int nranges)
{
const struct regmap_range *r;
int i;
for (i = 0, r = ranges; i < nranges; i++, r++)
if (regmap_reg_in_range(reg, r))
return true;
return false;
}
EXPORT_SYMBOL_GPL(regmap_reg_in_ranges);
bool regmap_check_range_table(struct regmap *map, unsigned int reg,
const struct regmap_access_table *table)
{
/* Check "no ranges" first */
if (regmap_reg_in_ranges(reg, table->no_ranges, table->n_no_ranges))
return false;
/* In case zero "yes ranges" are supplied, any reg is OK */
if (!table->n_yes_ranges)
return true;
return regmap_reg_in_ranges(reg, table->yes_ranges,
table->n_yes_ranges);
}
EXPORT_SYMBOL_GPL(regmap_check_range_table);
bool regmap_writeable(struct regmap *map, unsigned int reg)
{
if (map->max_register && reg > map->max_register)
return false;
if (map->writeable_reg)
return map->writeable_reg(map->dev, reg);
if (map->wr_table)
return regmap_check_range_table(map, reg, map->wr_table);
return true;
}
. . . . . .
static inline void *_regmap_map_get_context(struct regmap *map)
{
return (map->bus) ? map : map->bus_context;
}
int _regmap_write(struct regmap *map, unsigned int reg,
unsigned int val)
{
int ret;
void *context = _regmap_map_get_context(map);
if (!regmap_writeable(map, reg))
return -EIO;
if (!map->cache_bypass && !map->defer_caching) {
ret = regcache_write(map, reg, val);
if (ret != 0)
return ret;
if (map->cache_only) {
map->cache_dirty = true;
return 0;
}
}
if (regmap_should_log(map))
dev_info(map->dev, "%x <= %x\n", reg, val);
trace_regmap_reg_write(map, reg, val);
return map->reg_write(context, reg, val);
}
/**
* regmap_write() - Write a value to a single register
*
* @map: Register map to write to
* @reg: Register to write to
* @val: Value to be written
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
ret = _regmap_write(map, reg, val);
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_write);
/**
* regmap_write_async() - Write a value to a single register asynchronously
*
* @map: Register map to write to
* @reg: Register to write to
* @val: Value to be written
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
{
int ret;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
map->async = true;
ret = _regmap_write(map, reg, val);
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_write_async);
像眾多 regmap 機制提供的裝置暫存器存取操作函數一樣,這兩個函數,在開始任何操作前先加了鎖,並在結束操作後解鎖,regmap 機制提供了對裝置暫存器的互斥存取。
regmap_write_async()
函數在加鎖後,將 map->async
賦值為 true
,並在解鎖前將其賦值為 false
,從 _regmap_write()
函數的實現來看,這裡的非同步寫疑似沒有工作。
regmap_write()
和 regmap_write_async()
函數都通過 _regmap_write()
函數完成對裝置暫存器的寫操作。
_regmap_write()
函數的執行過程是簡單的三步:
檢查要寫入的暫存器是否可寫。這裡的檢查按照裝置暫存器的讀寫屬性設定進行。首先,檢查暫存器是否超過了設定的暫存器,若超過了,則顯然不可寫;其次,當 writeable_reg
回撥函數設定時,由該回撥函數判斷;然後,當 wr_table
可寫暫存器表設定時,根據該表做判斷;否則,認為暫存器可寫。對於暫存器是否可寫的判斷,如果同時設定了 writeable_reg
回撥函數和 wr_table
可寫暫存器表,則前者的優先順序高於後者,後者將被忽略;如果兩者都沒有設定,則認為暫存器可寫。
使用了 cache,而沒開延遲 cache 時,將要寫入暫存器的值先寫入 cached。如果寫入失敗,則直接返回,否則繼續執行。如果設定了 map->cache_only
,則將 map->cache_dirty
置為 true
並返回,否則繼續執行。map->cache_only
標記表示不希望真正地寫裝置暫存器。
呼叫 struct regmap
的 reg_write
操作向裝置暫存器寫入值。
這裡的寫操作基本上是一個無條件的寫,即在寫入裝置暫存器之前,不會檢查快取中是否已經存在了相同值。
regcache_write()
函數定義 (位於 drivers/base/regmap/regcache.c
) 如下:
int regcache_write(struct regmap *map,
unsigned int reg, unsigned int value)
{
if (map->cache_type == REGCACHE_NONE)
return 0;
BUG_ON(!map->cache_ops);
if (!regmap_volatile(map, reg))
return map->cache_ops->write(map, reg, value);
return 0;
}
regcache_write()
函數,首先,檢查是否開啟了 cache,如果沒有則直接返回,否則繼續執行;其次,檢查要寫入的暫存器是否為 volatile 的,如果不是,則通過 cache 實現的 write
回撥寫入 cache,否則返回。
regmap_volatile()
函數用以檢查暫存器是否為 volatile 的,這個函數定義 (位於 drivers/base/regmap/regmap.c
) 如下:
bool regmap_readable(struct regmap *map, unsigned int reg)
{
if (!map->reg_read)
return false;
if (map->max_register && reg > map->max_register)
return false;
if (map->format.format_write)
return false;
if (map->readable_reg)
return map->readable_reg(map->dev, reg);
if (map->rd_table)
return regmap_check_range_table(map, reg, map->rd_table);
return true;
}
bool regmap_volatile(struct regmap *map, unsigned int reg)
{
if (!map->format.format_write && !regmap_readable(map, reg))
return false;
if (map->volatile_reg)
return map->volatile_reg(map->dev, reg);
if (map->volatile_table)
return regmap_check_range_table(map, reg, map->volatile_table);
if (map->cache_ops)
return false;
else
return true;
}
對暫存器是否為 volatile 的檢查,暗含著對它是否可讀的檢查。如果暫存器不是 volatile 的,會被認為是可快取的。regmap_volatile()
函數的檢查過程如下:
沒有定義 format_write
操作,同時暫存器不可讀,則認為暫存器不是 volatile 的。這裡有個坑。如果暫存器是隻寫的,比如 W1C 寫 1 清的暫存器等 (對於硬體裝置,這樣的暫存器比較常見),在這裡會被判定為非 volatile 的,如果開了快取即是可快取的。在 regmap_update_bits()
操作中會出問題
和對暫存器的 writable 判斷類似,先檢查設定的 volatile_reg
回撥操作,再檢查 volatile_table
表。
如果既沒有設定 volatile_reg
回撥操作,也沒有設定 volatile_table
表,則根據快取設定判斷。如果開了快取,則認為所有暫存器都是非 volatile 的,即都可以快取,否則都是 volatile 的。
在 regmap_readable()
函數中對於暫存器是否可讀的判斷,與對暫存器是否可寫的判斷類似。但多了對 map->reg_read
暫存器讀操作的檢查,及格式化寫的檢查。
這裡不再詳細分析 regmap_raw_write()
、regmap_noinc_write()
和 regmap_bulk_write()
等更復雜的裝置暫存器寫操作。
Linux 核心裝置驅動程式通過 regmap_read()
等函數讀裝置暫存器,相關的這些函數原型 (位於 include/linux/regmap.h
) 如下:
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
int regmap_raw_read(struct regmap *map, unsigned int reg,
void *val, size_t val_len);
int regmap_noinc_read(struct regmap *map, unsigned int reg,
void *val, size_t val_len);
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_count);
regmap_read()
函數同步地讀一個裝置暫存器,這個函數定義 (位於 drivers/base/regmap/regmap.c
) 如下:
static int _regmap_read(struct regmap *map, unsigned int reg,
unsigned int *val)
{
int ret;
void *context = _regmap_map_get_context(map);
if (!map->cache_bypass) {
ret = regcache_read(map, reg, val);
if (ret == 0)
return 0;
}
if (map->cache_only)
return -EBUSY;
if (!regmap_readable(map, reg))
return -EIO;
ret = map->reg_read(context, reg, val);
if (ret == 0) {
if (regmap_should_log(map))
dev_info(map->dev, "%x => %x\n", reg, *val);
trace_regmap_reg_read(map, reg, *val);
if (!map->cache_bypass)
regcache_write(map, reg, *val);
}
return ret;
}
/**
* regmap_read() - Read a value from a single register
*
* @map: Register map to read from
* @reg: Register to be read from
* @val: Pointer to store read value
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
int ret;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
ret = _regmap_read(map, reg, val);
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_read);
與 regmap_write()
函數類似, regmap_read()
函數,首先,對 regmap 加鎖;然後,呼叫 _regmap_read()
函數執行讀操作;最後,解鎖並返回。_regmap_read()
函數的執行過程是清晰的幾個步驟:
如果開啟了快取,則先從快取讀,如果成功則返回,否則繼續執行。
如果設定了 map->cache_only
,則報錯返回。裝置驅動程式掛起時,可以設定 map->cache_only
,以防止意外地對裝置暫存器讀寫。
判斷暫存器是否可讀,如果不可讀,則報錯返回,否則繼續執行。對於只寫的裝置暫存器,如果開啟了快取,在這個函數中將讀到上次寫入的值。在邏輯上,這樣的返回值不太合適。這個函數更好的實現方法,似乎是將暫存器是否可讀的判斷,放在從快取讀暫存器前面。
讀取裝置暫存器。
讀取裝置暫存器成功,且開啟了快取,則將讀取的值寫入快取。
從快取中讀取裝置暫存器的值的函數 regcache_read()
定義 (位於 drivers/base/regmap/regcache.c
) 如下:
int regcache_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
int ret;
if (map->cache_type == REGCACHE_NONE)
return -ENOSYS;
BUG_ON(!map->cache_ops);
if (!regmap_volatile(map, reg)) {
ret = map->cache_ops->read(map, reg, value);
if (ret == 0)
trace_regmap_reg_read_cache(map, reg, *value);
return ret;
}
return -EINVAL;
}
快取操作針對開啟了快取的 regmap 的非 volatile 的暫存器。在 regcache_read()
函數中,它從快取實現中讀取暫存器的值。
這裡不再詳細分析 regmap_raw_read()
、regmap_noinc_read()
和 regmap_bulk_read()
等更復雜的裝置暫存器讀操作。
Linux 核心裝置驅動程式通過 regmap_update_bits()
等函數更新裝置暫存器的特定位,相關的這些函數原型 (位於 include/linux/regmap.h
) 如下:
int regmap_update_bits_base(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force);
static inline int regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
{
return regmap_update_bits_base(map, reg, mask, val, NULL, false, false);
}
static inline int regmap_update_bits_async(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
{
return regmap_update_bits_base(map, reg, mask, val, NULL, true, false);
}
static inline int regmap_update_bits_check(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change)
{
return regmap_update_bits_base(map, reg, mask, val,
change, false, false);
}
static inline int
regmap_update_bits_check_async(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change)
{
return regmap_update_bits_base(map, reg, mask, val,
change, true, false);
}
static inline int regmap_write_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val)
{
return regmap_update_bits_base(map, reg, mask, val, NULL, false, true);
}
regmap_update_bits()
等函數傳入不同的引數呼叫 regmap_update_bits_base()
函數,後者定義 (位於 drivers/base/regmap/regmap.c
) 如下:
static int _regmap_update_bits(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool force_write)
{
int ret;
unsigned int tmp, orig;
if (change)
*change = false;
if (regmap_volatile(map, reg) && map->reg_update_bits) {
ret = map->reg_update_bits(map->bus_context, reg, mask, val);
if (ret == 0 && change)
*change = true;
} else {
ret = _regmap_read(map, reg, &orig);
if (ret != 0)
return ret;
tmp = orig & ~mask;
tmp |= val & mask;
if (force_write || (tmp != orig)) {
ret = _regmap_write(map, reg, tmp);
if (ret == 0 && change)
*change = true;
}
}
return ret;
}
/**
* regmap_update_bits_base() - Perform a read/modify/write cycle on a register
*
* @map: Register map to update
* @reg: Register to update
* @mask: Bitmask to change
* @val: New value for bitmask
* @change: Boolean indicating if a write was done
* @async: Boolean indicating asynchronously
* @force: Boolean indicating use force update
*
* Perform a read/modify/write cycle on a register map with change, async, force
* options.
*
* If async is true:
*
* With most buses the read must be done synchronously so this is most useful
* for devices with a cache which do not need to interact with the hardware to
* determine the current register value.
*
* Returns zero for success, a negative number on error.
*/
int regmap_update_bits_base(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force)
{
int ret;
map->lock(map->lock_arg);
map->async = async;
ret = _regmap_update_bits(map, reg, mask, val, change, force);
map->async = false;
map->unlock(map->lock_arg);
return ret;
}
EXPORT_SYMBOL_GPL(regmap_update_bits_base);
與 regmap_write()
和 regmap_read()
函數類似,regmap_update_bits_base()
函數,首先,對 regmap 加鎖;然後,設定 map->async
標誌,呼叫 _regmap_update_bits()
函數執行暫存器位更新操作;最後,重置 map->async
標誌,解鎖並返回。_regmap_update_bits()
函數的執行分成幾種情況來處理:
暫存器為 volatile 的,同時設定了 reg_update_bits
回撥函數,則執行 reg_update_bits
回撥函數並返回結果。暫存器為 volatile 的,所以可以忽略對 cached 的操作。只寫暫存器會被判定為非 volatile 的,因而它們不會由 reg_update_bits
回撥函數處理。
其它情況。先讀取暫存器。特別需要關注的是對只寫暫存器的處理。第一次讀取只寫暫存器時,會讀取失敗並返回錯誤。如果之前對只寫暫存器有過寫入操作,且開了 cache,則會讀取之前寫入的值。隨後,如果要求強制寫,或要寫入的位的值與讀取的值不同,則將值寫入暫存器。如果開了 cache,寫操作會更新 cache。
考慮只寫暫存器通過 regmap_update_bits_base()
函數來更新,則要麼更新失敗,要麼很可能發現要更新的值和快取中的值一致,而不會實際去更新。對只寫暫存器的任何更新,regmap_write()
或 regmap_write_bits()
函數是更好的選擇。
要使得對 regmap 各函數呼叫的行為符合預期,還是需要對這些函數的行為實現有所瞭解,並適當的設定驅動程式中各個暫存器的讀寫屬性。
整體看下來,regmap 機制提供的能力有這樣一些:
struct regmap_bus
即可。Done.