2008-07-30 12:06:12 -07:00
/**************************************************************************
*
* Copyright ( c ) 2006 - 2007 Tungsten Graphics , Inc . , Cedar Park , TX . , USA
* All Rights Reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the
* " Software " ) , to deal in the Software without restriction , including
* without limitation the rights to use , copy , modify , merge , publish ,
* distribute , sub license , and / or sell copies of the Software , and to
* permit persons to whom the Software is furnished to do so , subject to
* the following conditions :
*
* The above copyright notice and this permission notice ( including the
* next paragraph ) shall be included in all copies or substantial portions
* of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NON - INFRINGEMENT . IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS , AUTHORS AND / OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM ,
* DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR
* OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Authors : Thomas Hellström < thomas - at - tungstengraphics - dot - com >
*/
2022-02-04 09:05:41 -08:00
# include <linux/cc_platform.h>
2011-08-30 18:16:33 -04:00
# include <linux/export.h>
2017-01-21 18:19:44 +00:00
# include <linux/highmem.h>
2022-02-16 15:31:36 +11:00
# include <linux/ioport.h>
2022-02-04 09:05:41 -08:00
# include <linux/iosys-map.h>
2021-01-12 09:10:31 +01:00
# include <xen/xen.h>
2017-01-21 18:19:44 +00:00
# include <drm/drm_cache.h>
2008-07-30 12:06:12 -07:00
2021-06-02 10:38:11 +02:00
/* A small bounce buffer that fits on the stack. */
# define MEMCPY_BOUNCE_SIZE 128
2008-07-30 12:06:12 -07:00
# if defined(CONFIG_X86)
2014-12-15 12:26:46 -08:00
# include <asm/smp.h>
2014-02-26 12:06:51 -07:00
/*
* clflushopt is an unordered instruction which needs fencing with mfence or
* sfence to avoid ordering issues . For drm_clflush_page this fencing happens
* in the caller .
*/
2008-07-30 12:06:12 -07:00
static void
drm_clflush_page ( struct page * page )
{
uint8_t * page_virtual ;
unsigned int i ;
2012-09-19 11:12:41 +10:00
const int size = boot_cpu_data . x86_clflush_size ;
2008-07-30 12:06:12 -07:00
if ( unlikely ( page = = NULL ) )
return ;
2011-11-25 23:14:20 +08:00
page_virtual = kmap_atomic ( page ) ;
2012-09-19 11:12:41 +10:00
for ( i = 0 ; i < PAGE_SIZE ; i + = size )
2014-02-26 12:06:51 -07:00
clflushopt ( page_virtual + i ) ;
2011-11-25 23:14:20 +08:00
kunmap_atomic ( page_virtual ) ;
2008-07-30 12:06:12 -07:00
}
2009-08-27 09:53:47 +10:00
static void drm_cache_flush_clflush ( struct page * pages [ ] ,
unsigned long num_pages )
{
unsigned long i ;
2019-10-24 09:58:33 +05:30
mb ( ) ; /*Full memory barrier used before so that CLFLUSH is ordered*/
2009-08-27 09:53:47 +10:00
for ( i = 0 ; i < num_pages ; i + + )
drm_clflush_page ( * pages + + ) ;
2019-10-24 09:58:33 +05:30
mb ( ) ; /*Also used after CLFLUSH so that all cache is flushed*/
2009-08-27 09:53:47 +10:00
}
# endif
2009-09-02 09:41:13 +10:00
2017-01-09 19:56:48 -02:00
/**
* drm_clflush_pages - Flush dcache lines of a set of pages .
* @ pages : List of pages to be flushed .
* @ num_pages : Number of pages in the array .
*
* Flush every data cache line entry that points to an address belonging
* to a page in the array .
*/
2008-07-30 12:06:12 -07:00
void
drm_clflush_pages ( struct page * pages [ ] , unsigned long num_pages )
{
# if defined(CONFIG_X86)
2016-03-29 17:41:59 +02:00
if ( static_cpu_has ( X86_FEATURE_CLFLUSH ) ) {
2009-08-27 09:53:47 +10:00
drm_cache_flush_clflush ( pages , num_pages ) ;
2008-07-30 12:06:12 -07:00
return ;
}
2014-12-15 12:26:46 -08:00
if ( wbinvd_on_all_cpus ( ) )
2017-02-28 04:55:53 -08:00
pr_err ( " Timed out waiting for cache flush \n " ) ;
2009-08-27 09:53:47 +10:00
# elif defined(__powerpc__)
unsigned long i ;
2019-10-25 11:57:13 +05:30
2009-08-27 09:53:47 +10:00
for ( i = 0 ; i < num_pages ; i + + ) {
struct page * page = pages [ i ] ;
void * page_virtual ;
if ( unlikely ( page = = NULL ) )
continue ;
2011-11-25 23:14:20 +08:00
page_virtual = kmap_atomic ( page ) ;
2009-08-27 09:53:47 +10:00
flush_dcache_range ( ( unsigned long ) page_virtual ,
( unsigned long ) page_virtual + PAGE_SIZE ) ;
2011-11-25 23:14:20 +08:00
kunmap_atomic ( page_virtual ) ;
2009-08-27 09:53:47 +10:00
}
# else
2022-01-31 08:59:24 -08:00
WARN_ONCE ( 1 , " Architecture has no drm_cache.c support \n " ) ;
2008-10-07 13:41:49 +10:00
# endif
2008-07-30 12:06:12 -07:00
}
EXPORT_SYMBOL ( drm_clflush_pages ) ;
2012-03-25 19:47:30 +02:00
2017-01-09 19:56:48 -02:00
/**
* drm_clflush_sg - Flush dcache lines pointing to a scather - gather .
* @ st : struct sg_table .
*
* Flush every data cache line entry that points to an address in the
* sg .
*/
2012-06-01 15:20:22 +01:00
void
drm_clflush_sg ( struct sg_table * st )
{
# if defined(CONFIG_X86)
2016-03-29 17:41:59 +02:00
if ( static_cpu_has ( X86_FEATURE_CLFLUSH ) ) {
2013-02-18 19:28:01 +02:00
struct sg_page_iter sg_iter ;
2012-06-01 15:20:22 +01:00
2019-10-25 11:57:38 +05:30
mb ( ) ; /*CLFLUSH is ordered only by using memory barriers*/
2020-05-11 12:27:54 +02:00
for_each_sgtable_page ( st , & sg_iter , 0 )
2013-03-26 15:14:18 +02:00
drm_clflush_page ( sg_page_iter_page ( & sg_iter ) ) ;
2019-10-25 11:57:38 +05:30
mb ( ) ; /*Make sure that all cache line entry is flushed*/
2012-06-01 15:20:22 +01:00
return ;
}
2014-12-15 12:26:46 -08:00
if ( wbinvd_on_all_cpus ( ) )
2017-02-28 04:55:53 -08:00
pr_err ( " Timed out waiting for cache flush \n " ) ;
2012-06-01 15:20:22 +01:00
# else
2022-01-31 08:59:24 -08:00
WARN_ONCE ( 1 , " Architecture has no drm_cache.c support \n " ) ;
2012-06-01 15:20:22 +01:00
# endif
}
EXPORT_SYMBOL ( drm_clflush_sg ) ;
2017-01-09 19:56:48 -02:00
/**
* drm_clflush_virt_range - Flush dcache lines of a region
* @ addr : Initial kernel memory address .
* @ length : Region size .
*
* Flush every data cache line entry that points to an address in the
* region requested .
*/
2012-03-25 19:47:30 +02:00
void
2014-04-01 12:59:08 +03:00
drm_clflush_virt_range ( void * addr , unsigned long length )
2012-03-25 19:47:30 +02:00
{
# if defined(CONFIG_X86)
2016-03-29 17:41:59 +02:00
if ( static_cpu_has ( X86_FEATURE_CLFLUSH ) ) {
2015-06-10 15:58:01 +01:00
const int size = boot_cpu_data . x86_clflush_size ;
2014-04-01 12:59:08 +03:00
void * end = addr + length ;
2019-10-25 11:57:13 +05:30
2015-06-10 15:58:01 +01:00
addr = ( void * ) ( ( ( unsigned long ) addr ) & - size ) ;
2019-10-25 11:57:38 +05:30
mb ( ) ; /*CLFLUSH is only ordered with a full memory barrier*/
2015-06-10 15:58:01 +01:00
for ( ; addr < end ; addr + = size )
2014-05-14 09:41:12 -06:00
clflushopt ( addr ) ;
2016-07-07 09:41:12 +01:00
clflushopt ( end - 1 ) ; /* force serialisation */
2021-07-30 21:27:29 +08:00
mb ( ) ; /*Ensure that every data cache line entry is flushed*/
2012-03-25 19:47:30 +02:00
return ;
}
2014-12-15 12:26:46 -08:00
if ( wbinvd_on_all_cpus ( ) )
2017-02-28 04:55:53 -08:00
pr_err ( " Timed out waiting for cache flush \n " ) ;
2012-03-25 19:47:30 +02:00
# else
2022-01-31 08:59:24 -08:00
WARN_ONCE ( 1 , " Architecture has no drm_cache.c support \n " ) ;
2012-03-25 19:47:30 +02:00
# endif
}
EXPORT_SYMBOL ( drm_clflush_virt_range ) ;
2021-01-12 09:10:31 +01:00
bool drm_need_swiotlb ( int dma_bits )
{
struct resource * tmp ;
resource_size_t max_iomem = 0 ;
/*
* Xen paravirtual hosts require swiotlb regardless of requested dma
* transfer size .
*
* NOTE : Really , what it requires is use of the dma_alloc_coherent
* allocator used in ttm_dma_populate ( ) instead of
* ttm_populate_and_map_pages ( ) , which bounce buffers so much in
* Xen it leads to swiotlb buffer exhaustion .
*/
if ( xen_pv_domain ( ) )
return true ;
/*
* Enforce dma_alloc_coherent when memory encryption is active as well
* for the same reasons as for Xen paravirtual hosts .
*/
2021-09-08 17:58:39 -05:00
if ( cc_platform_has ( CC_ATTR_MEM_ENCRYPT ) )
2021-01-12 09:10:31 +01:00
return true ;
for ( tmp = iomem_resource . child ; tmp ; tmp = tmp - > sibling )
max_iomem = max ( max_iomem , tmp - > end ) ;
return max_iomem > ( ( u64 ) 1 < < dma_bits ) ;
}
EXPORT_SYMBOL ( drm_need_swiotlb ) ;
2021-06-02 10:38:11 +02:00
2022-02-04 09:05:41 -08:00
static void memcpy_fallback ( struct iosys_map * dst ,
const struct iosys_map * src ,
2021-06-02 10:38:11 +02:00
unsigned long len )
{
if ( ! dst - > is_iomem & & ! src - > is_iomem ) {
memcpy ( dst - > vaddr , src - > vaddr , len ) ;
} else if ( ! src - > is_iomem ) {
2022-02-16 09:41:32 -08:00
iosys_map_memcpy_to ( dst , 0 , src - > vaddr , len ) ;
2021-06-02 10:38:11 +02:00
} else if ( ! dst - > is_iomem ) {
memcpy_fromio ( dst - > vaddr , src - > vaddr_iomem , len ) ;
} else {
/*
* Bounce size is not performance tuned , but using a
* bounce buffer like this is significantly faster than
* resorting to ioreadxx ( ) + iowritexx ( ) .
*/
char bounce [ MEMCPY_BOUNCE_SIZE ] ;
void __iomem * _src = src - > vaddr_iomem ;
void __iomem * _dst = dst - > vaddr_iomem ;
while ( len > = MEMCPY_BOUNCE_SIZE ) {
memcpy_fromio ( bounce , _src , MEMCPY_BOUNCE_SIZE ) ;
memcpy_toio ( _dst , bounce , MEMCPY_BOUNCE_SIZE ) ;
_src + = MEMCPY_BOUNCE_SIZE ;
_dst + = MEMCPY_BOUNCE_SIZE ;
len - = MEMCPY_BOUNCE_SIZE ;
}
if ( len ) {
memcpy_fromio ( bounce , _src , MEMCPY_BOUNCE_SIZE ) ;
memcpy_toio ( _dst , bounce , MEMCPY_BOUNCE_SIZE ) ;
}
}
}
# ifdef CONFIG_X86
static DEFINE_STATIC_KEY_FALSE ( has_movntdqa ) ;
static void __memcpy_ntdqa ( void * dst , const void * src , unsigned long len )
{
kernel_fpu_begin ( ) ;
while ( len > = 4 ) {
asm ( " movntdqa (%0), %%xmm0 \n "
" movntdqa 16(%0), %%xmm1 \n "
" movntdqa 32(%0), %%xmm2 \n "
" movntdqa 48(%0), %%xmm3 \n "
" movaps %%xmm0, (%1) \n "
" movaps %%xmm1, 16(%1) \n "
" movaps %%xmm2, 32(%1) \n "
" movaps %%xmm3, 48(%1) \n "
: : " r " ( src ) , " r " ( dst ) : " memory " ) ;
src + = 64 ;
dst + = 64 ;
len - = 4 ;
}
while ( len - - ) {
asm ( " movntdqa (%0), %%xmm0 \n "
" movaps %%xmm0, (%1) \n "
: : " r " ( src ) , " r " ( dst ) : " memory " ) ;
src + = 16 ;
dst + = 16 ;
}
kernel_fpu_end ( ) ;
}
/*
* __drm_memcpy_from_wc copies @ len bytes from @ src to @ dst using
* non - temporal instructions where available . Note that all arguments
* ( @ src , @ dst ) must be aligned to 16 bytes and @ len must be a multiple
* of 16.
*/
static void __drm_memcpy_from_wc ( void * dst , const void * src , unsigned long len )
{
if ( unlikely ( ( ( unsigned long ) dst | ( unsigned long ) src | len ) & 15 ) )
memcpy ( dst , src , len ) ;
else if ( likely ( len ) )
__memcpy_ntdqa ( dst , src , len > > 4 ) ;
}
/**
* drm_memcpy_from_wc - Perform the fastest available memcpy from a source
* that may be WC .
* @ dst : The destination pointer
* @ src : The source pointer
* @ len : The size of the area o transfer in bytes
*
* Tries an arch optimized memcpy for prefetching reading out of a WC region ,
* and if no such beast is available , falls back to a normal memcpy .
*/
2022-02-04 09:05:41 -08:00
void drm_memcpy_from_wc ( struct iosys_map * dst ,
const struct iosys_map * src ,
2021-06-02 10:38:11 +02:00
unsigned long len )
{
if ( WARN_ON ( in_interrupt ( ) ) ) {
memcpy_fallback ( dst , src , len ) ;
return ;
}
if ( static_branch_likely ( & has_movntdqa ) ) {
__drm_memcpy_from_wc ( dst - > is_iomem ?
( void __force * ) dst - > vaddr_iomem :
dst - > vaddr ,
src - > is_iomem ?
( void const __force * ) src - > vaddr_iomem :
src - > vaddr ,
len ) ;
return ;
}
memcpy_fallback ( dst , src , len ) ;
}
EXPORT_SYMBOL ( drm_memcpy_from_wc ) ;
/*
* drm_memcpy_init_early - One time initialization of the WC memcpy code
*/
void drm_memcpy_init_early ( void )
{
/*
* Some hypervisors ( e . g . KVM ) don ' t support VEX - prefix instructions
* emulation . So don ' t enable movntdqa in hypervisor guest .
*/
if ( static_cpu_has ( X86_FEATURE_XMM4_1 ) & &
! boot_cpu_has ( X86_FEATURE_HYPERVISOR ) )
static_branch_enable ( & has_movntdqa ) ;
}
# else
2022-02-04 09:05:41 -08:00
void drm_memcpy_from_wc ( struct iosys_map * dst ,
const struct iosys_map * src ,
2021-06-02 10:38:11 +02:00
unsigned long len )
{
WARN_ON ( in_interrupt ( ) ) ;
memcpy_fallback ( dst , src , len ) ;
}
EXPORT_SYMBOL ( drm_memcpy_from_wc ) ;
void drm_memcpy_init_early ( void )
{
}
# endif /* CONFIG_X86 */