Commit 30052002 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents dd09eb44 09e70e4f
Loading
Loading
Loading
Loading
+26 −4
Original line number Diff line number Diff line
@@ -1545,6 +1545,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
			       unsigned int val_num)
{
	void *orig_work_buf;
	unsigned int selector_reg;
	unsigned int win_offset;
	unsigned int win_page;
	bool page_chg;
@@ -1563,10 +1564,31 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
			return -EINVAL;
	}

	/* 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. */
	/*
	 * Calculate the address of the selector register in the corresponding
	 * 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 ||
	    (page_chg && selector_reg != range->selector_reg) ||
	    range->window_start + win_offset != range->selector_reg) {
		/* Use separate work_buf during page switching */
		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,
					  range->selector_mask,
					  win_page << range->selector_shift,
					  &page_chg, false);
					  NULL, false);

		map->work_buf = orig_work_buf;