Skip to content

Commit 1ef3e1c

Browse files
andy-shevbroonie
authored andcommitted
regmap: Synchronize cache for the page selector
If the selector register is represented in each page, its value according to the debugfs is stale because it gets synchronized only after the real page switch happens. Hence the regmap cache initialisation from the HW inherits outdated data in the selector register. Synchronize cache for the page selector just in time. Before (offset followed by hexdump, the first byte is selector): // Real registers 18: 05 ff 00 00 ff 0f 00 00 f0 00 00 00 ... // Virtual (per port) 40: 05 ff 00 00 e0 e0 00 00 00 00 00 1f 50: 00 ff 00 00 e0 e0 00 00 00 00 00 1f 60: 01 ff 00 00 ff ff 00 00 00 00 00 00 70: 02 ff 00 00 cf f3 00 00 00 00 00 0c 80: 03 ff 00 00 00 00 00 00 00 00 00 ff 90: 04 ff 00 00 ff 0f 00 00 f0 00 00 00 After: // Real registers 18: 05 ff 00 00 ff 0f 00 00 f0 00 00 00 ... // Virtual (per port) 40: 00 ff 00 00 e0 e0 00 00 00 00 00 1f 50: 01 ff 00 00 e0 e0 00 00 00 00 00 1f 60: 02 ff 00 00 ff ff 00 00 00 00 00 00 70: 03 ff 00 00 cf f3 00 00 00 00 00 0c 80: 04 ff 00 00 00 00 00 00 00 00 00 ff 90: 05 ff 00 00 ff 0f 00 00 f0 00 00 00 Fixes: 6863ca6 ("regmap: Add support for register indirect addressing.") Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://patch.msgid.link/20260302184753.2693803-1-andriy.shevchenko@linux.intel.com Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent b1ef855 commit 1ef3e1c

1 file changed

Lines changed: 26 additions & 4 deletions

File tree

drivers/base/regmap/regmap.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
15421542
unsigned int val_num)
15431543
{
15441544
void *orig_work_buf;
1545+
unsigned int selector_reg;
15451546
unsigned int win_offset;
15461547
unsigned int win_page;
15471548
bool page_chg;
@@ -1560,10 +1561,31 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
15601561
return -EINVAL;
15611562
}
15621563

1563-
/* It is possible to have selector register inside data window.
1564-
In that case, selector register is located on every page and
1565-
it needs no page switching, when accessed alone. */
1564+
/*
1565+
* Calculate the address of the selector register in the corresponding
1566+
* data window if it is located on every page.
1567+
*/
1568+
page_chg = in_range(range->selector_reg, range->window_start, range->window_len);
1569+
if (page_chg)
1570+
selector_reg = range->range_min + win_page * range->window_len +
1571+
range->selector_reg - range->window_start;
1572+
1573+
/*
1574+
* It is possible to have selector register inside data window.
1575+
* In that case, selector register is located on every page and it
1576+
* needs no page switching, when accessed alone.
1577+
*
1578+
* Nevertheless we should synchronize the cache values for it.
1579+
* This can't be properly achieved if the selector register is
1580+
* the first and the only one to be read inside the data window.
1581+
* That's why we update it in that case as well.
1582+
*
1583+
* However, we specifically avoid updating it for the default page,
1584+
* when it's overlapped with the real data window, to prevent from
1585+
* infinite looping.
1586+
*/
15661587
if (val_num > 1 ||
1588+
(page_chg && selector_reg != range->selector_reg) ||
15671589
range->window_start + win_offset != range->selector_reg) {
15681590
/* Use separate work_buf during page switching */
15691591
orig_work_buf = map->work_buf;
@@ -1572,7 +1594,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
15721594
ret = _regmap_update_bits(map, range->selector_reg,
15731595
range->selector_mask,
15741596
win_page << range->selector_shift,
1575-
&page_chg, false);
1597+
NULL, false);
15761598

15771599
map->work_buf = orig_work_buf;
15781600

0 commit comments

Comments
 (0)