Commit 3cc2dac5 authored by Luis R. Rodriguez's avatar Luis R. Rodriguez Committed by Ingo Molnar

drivers/video/fbdev/atyfb: Replace MTRR UC hole with strong UC

Replace a WC MTRR call followed by a UC MTRR "hole" call with a
single WC MTRR call and use strong UC to protect the MMIO region
and account for the device's architecture and MTRR size
requirements.

The atyfb driver relies on two overlapping MTRRs. It does this
to account for the fact that, on some devices, it has the MMIO
region bundled together with the framebuffer on the same PCI BAR
and the hardware requirement on MTRRs on both base and size to
be powers of two.

In the worst case, the PCI BAR is of 16 MiB while the MMIO
region is on the last 4 KiB of the same PCI BAR. If we use just
one MTRR for WC, we can only end up with an 8 MiB or 16 MiB
framebuffer. Using a 16 MiB WC framebuffer area is unacceptable
since we need the MMIO region to not be write-combined. An 8 MiB
WC framebuffer option does not let use quite a bit of
framebuffer space, it would reduce the resolution capability of
the device considerably.

An alternative is to use many MTRRs but on some systems that
could mean not having enough MTRRs to cover the framebuffer. The
current solution is to issue a 16 MiB WC MTRR followed by a 4
KiB UC MTRR on the last 4 KiB. Its worth mentioning and
documenting that the current ioremap*() strategy as well: the
first ioremap() is used only for the MMIO region, a second
ioremap() call is used for the framebuffer *and* the MMIO
region, the MMIO region then ends up mmapped twice.

Two ioremap() calls are used since in some situations the
framebuffer actually ends up on a separate auxiliary PCI BAR,
but this is not always true. In the worst case, the PCI BAR is
shared for both MMIO and the framebuffer. By allowing
overlapping ioremap() calls, the driver enables two types of
devices with one simple ioremap() strategy.

See also:

  2f9e8973 ("x86/mm/mtrr, pat: Document Write Combining MTRR type effects on PAT / non-PAT pages")

By default, Linux today defaults both pci_mmap_page_range() and
ioremap_nocache() to use _PAGE_CACHE_MODE_UC_MINUS. On x86,
ioremap() aliases ioremap_nocache(). The preferred value for
Linux may soon change, however, the goal is to use
_PAGE_CACHE_MODE_UC by default in the future.

We can use ioremap_uc() to set PCD=1, PWT=1 on non-PAT systems
and use a PAT value of UC for PAT systems. This will ensure the
same settings are in place regardless of what Linux decides to
use by default later and to not regress our MTRR strategy since
the effective memory type will differ depending on the value
used. Using a WC MTRR on such an area will be nullified. This
technique can be used to protect the MMIO region in this
driver's case and address the restrictions of the device's
architecture as well as restrictions set upon us by powers of 2
when using MTRRs.

This allows us to replace the two MTRR calls with a single 16
MiB WC MTRR and use page-attribute settings for non-PAT and PAT
entry values for PAT systems to ensure the appropriate effective
memory type won't have a write-combining effect on the MMIO
region on both non-PAT and PAT systems. The framebuffer area
will be sure to get the write-combined effective memory type by
white-listing it with ioremap_wc().

We ensure the desired effective memory types are set by:

0) Using one ioremap_uc() for the MMIO region alone.
   This will set the page attribute settings for the MMIO
   region to PCD=1, PWT=1 for non-PAT systems while using a
   strong UC value on PAT systems.

1) Fixing the framebuffer ioremapped area to exclude the
   MMIO region and using ioremap_wc() instead to whitelist
   the area we want for write-combining.

In both cases, an implementation defined (as per 2f9e8973)
effective memory type of WC is used for the framebuffer for
non-PAT systems.
Signed-off-by: default avatarLuis R. Rodriguez <mcgrof@suse.com>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andrzej Hajda <a.hajda@samsung.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Antonino Daplas <adaplas@gmail.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Dave Airlie <airlied@redhat.com>
Cc: Davidlohr Bueso <dbueso@suse.de>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
Cc: Juergen Gross <jgross@suse.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mathias Krause <minipli@googlemail.com>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rob Clark <robdclark@gmail.com>
Cc: Suresh Siddha <sbsiddha@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Toshi Kani <toshi.kani@hp.com>
Cc: Ville Syrjälä <syrjala@sci.fi>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: arnd@arndb.de
Cc: benh@kernel.crashing.org
Cc: dan.j.williams@intel.com
Cc: geert@linux-m68k.org
Cc: hch@lst.de
Cc: hmh@hmh.eng.br
Cc: linux-fbdev@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: linux-pci@vger.kernel.org
Cc: mpe@ellerman.id.au
Cc: mst@redhat.com
Cc: ralf@linux-mips.org
Cc: ross.zwisler@linux.intel.com
Cc: stefan.bader@canonical.com
Cc: tj@kernel.org
Cc: ville.syrjala@linux.intel.com
Link: http://lkml.kernel.org/r/1435196060-27350-3-git-send-email-mcgrof@do-not-panic.com
Link: http://lkml.kernel.org/r/1436491499-3289-4-git-send-email-mcgrof@do-not-panic.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent f55de6ec
...@@ -184,7 +184,6 @@ struct atyfb_par { ...@@ -184,7 +184,6 @@ struct atyfb_par {
spinlock_t int_lock; spinlock_t int_lock;
#ifdef CONFIG_MTRR #ifdef CONFIG_MTRR
int mtrr_aper; int mtrr_aper;
int mtrr_reg;
#endif #endif
u32 mem_cntl; u32 mem_cntl;
struct crtc saved_crtc; struct crtc saved_crtc;
......
...@@ -2630,21 +2630,13 @@ static int aty_init(struct fb_info *info) ...@@ -2630,21 +2630,13 @@ static int aty_init(struct fb_info *info)
#ifdef CONFIG_MTRR #ifdef CONFIG_MTRR
par->mtrr_aper = -1; par->mtrr_aper = -1;
par->mtrr_reg = -1;
if (!nomtrr) { if (!nomtrr) {
/* Cover the whole resource. */ /*
* Only the ioremap_wc()'d area will get WC here
* since ioremap_uc() was used on the entire PCI BAR.
*/
par->mtrr_aper = mtrr_add(par->res_start, par->res_size, par->mtrr_aper = mtrr_add(par->res_start, par->res_size,
MTRR_TYPE_WRCOMB, 1); MTRR_TYPE_WRCOMB, 1);
if (par->mtrr_aper >= 0 && !par->aux_start) {
/* Make a hole for mmio. */
par->mtrr_reg = mtrr_add(par->res_start + 0x800000 -
GUI_RESERVE, GUI_RESERVE,
MTRR_TYPE_UNCACHABLE, 1);
if (par->mtrr_reg < 0) {
mtrr_del(par->mtrr_aper, 0, 0);
par->mtrr_aper = -1;
}
}
} }
#endif #endif
...@@ -2776,10 +2768,6 @@ static int aty_init(struct fb_info *info) ...@@ -2776,10 +2768,6 @@ static int aty_init(struct fb_info *info)
par->pll_ops->set_pll(info, &par->saved_pll); par->pll_ops->set_pll(info, &par->saved_pll);
#ifdef CONFIG_MTRR #ifdef CONFIG_MTRR
if (par->mtrr_reg >= 0) {
mtrr_del(par->mtrr_reg, 0, 0);
par->mtrr_reg = -1;
}
if (par->mtrr_aper >= 0) { if (par->mtrr_aper >= 0) {
mtrr_del(par->mtrr_aper, 0, 0); mtrr_del(par->mtrr_aper, 0, 0);
par->mtrr_aper = -1; par->mtrr_aper = -1;
...@@ -3466,7 +3454,11 @@ static int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, ...@@ -3466,7 +3454,11 @@ static int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info,
} }
info->fix.mmio_start = raddr; info->fix.mmio_start = raddr;
par->ati_regbase = ioremap(info->fix.mmio_start, 0x1000); /*
* By using strong UC we force the MTRR to never have an
* effect on the MMIO region on both non-PAT and PAT systems.
*/
par->ati_regbase = ioremap_uc(info->fix.mmio_start, 0x1000);
if (par->ati_regbase == NULL) if (par->ati_regbase == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -3503,7 +3495,10 @@ static int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, ...@@ -3503,7 +3495,10 @@ static int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info,
*/ */
info->fix.smem_len = 0x800000; info->fix.smem_len = 0x800000;
info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); aty_fudge_framebuffer_len(info);
info->screen_base = ioremap_wc(info->fix.smem_start,
info->fix.smem_len);
if (info->screen_base == NULL) { if (info->screen_base == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto atyfb_setup_generic_fail; goto atyfb_setup_generic_fail;
...@@ -3575,6 +3570,7 @@ static int atyfb_pci_probe(struct pci_dev *pdev, ...@@ -3575,6 +3570,7 @@ static int atyfb_pci_probe(struct pci_dev *pdev,
return -ENOMEM; return -ENOMEM;
} }
par = info->par; par = info->par;
par->bus_type = PCI;
info->fix = atyfb_fix; info->fix = atyfb_fix;
info->device = &pdev->dev; info->device = &pdev->dev;
par->pci_id = pdev->device; par->pci_id = pdev->device;
...@@ -3744,10 +3740,6 @@ static void atyfb_remove(struct fb_info *info) ...@@ -3744,10 +3740,6 @@ static void atyfb_remove(struct fb_info *info)
#endif #endif
#ifdef CONFIG_MTRR #ifdef CONFIG_MTRR
if (par->mtrr_reg >= 0) {
mtrr_del(par->mtrr_reg, 0, 0);
par->mtrr_reg = -1;
}
if (par->mtrr_aper >= 0) { if (par->mtrr_aper >= 0) {
mtrr_del(par->mtrr_aper, 0, 0); mtrr_del(par->mtrr_aper, 0, 0);
par->mtrr_aper = -1; par->mtrr_aper = -1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment