regmap: Fix for v7.0

A fix from Andy Shevchenko for an issue with caching of page selector
 registers which are located inside the page they are switching.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmnGzFoACgkQJNaLcl1U
 h9CIHgf9EUlo+kHud/8a2NPDZAQK9OHpfEGd5vHGemd9H2NUbKuq3wwHYSaKdF4x
 W+71HKr5GBIOG5hdmpQ2eynKtNdsvmLaTvjOtO4W8eJzI7hFR0I+O4dWEnJe5My9
 tHciXphh515LXud7h5qGX04QZSzD0NzntVxDJyGGmLtSwQwJjIIi6mqCzPY1BJ9h
 +adQNYqj9DXM9EVSsRsYhcxc64QzyXK4ThW6/BNOEVGTuBOKv6AjnoDvEG8xVg+L
 /8Af01CwLS8vqlVl8nd9TyJOuIGW2GoeYjxkbvsRwEapF/bcnPyPU3fnEb653dxm
 H9tAsSrrUJ2Y+rzZpZdXmPyuJvD4SQ==
 =/tG2
 -----END PGP SIGNATURE-----

Merge tag 'regmap-fix-v7.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

Pull regmap fix from Mark Brown:
 "A fix from Andy Shevchenko for an issue with caching of page selector
  registers which are located inside the page they are switching"

* tag 'regmap-fix-v7.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: Synchronize cache for the page selector
This commit is contained in:
Linus Torvalds 2026-03-27 16:34:25 -07:00
commit 30052002e6
1 changed files with 26 additions and 4 deletions

View File

@ -1545,6 +1545,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
unsigned int val_num) unsigned int val_num)
{ {
void *orig_work_buf; void *orig_work_buf;
unsigned int selector_reg;
unsigned int win_offset; unsigned int win_offset;
unsigned int win_page; unsigned int win_page;
bool page_chg; bool page_chg;
@ -1563,10 +1564,31 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
return -EINVAL; return -EINVAL;
} }
/* It is possible to have selector register inside data window. /*
In that case, selector register is located on every page and * Calculate the address of the selector register in the corresponding
it needs no page switching, when accessed alone. */ * data window if it is located on every page.
*/
page_chg = in_range(range->selector_reg, range->window_start, range->window_len);
if (page_chg)
selector_reg = range->range_min + win_page * range->window_len +
range->selector_reg - range->window_start;
/*
* It is possible to have selector register inside data window.
* In that case, selector register is located on every page and it
* needs no page switching, when accessed alone.
*
* Nevertheless we should synchronize the cache values for it.
* This can't be properly achieved if the selector register is
* the first and the only one to be read inside the data window.
* That's why we update it in that case as well.
*
* However, we specifically avoid updating it for the default page,
* when it's overlapped with the real data window, to prevent from
* infinite looping.
*/
if (val_num > 1 || if (val_num > 1 ||
(page_chg && selector_reg != range->selector_reg) ||
range->window_start + win_offset != range->selector_reg) { range->window_start + win_offset != range->selector_reg) {
/* Use separate work_buf during page switching */ /* Use separate work_buf during page switching */
orig_work_buf = map->work_buf; orig_work_buf = map->work_buf;
@ -1575,7 +1597,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
ret = _regmap_update_bits(map, range->selector_reg, ret = _regmap_update_bits(map, range->selector_reg,
range->selector_mask, range->selector_mask,
win_page << range->selector_shift, win_page << range->selector_shift,
&page_chg, false); NULL, false);
map->work_buf = orig_work_buf; map->work_buf = orig_work_buf;