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:
commit
30052002e6
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue