2009-05-14 16:42:28 +04:00
/*
* Contains routines needed to support swiotlb for ppc .
*
2010-05-03 16:36:22 +04:00
* Copyright ( C ) 2009 - 2010 Freescale Semiconductor , Inc .
* Author : Becky Bruce
2009-05-14 16:42:28 +04:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
*/
# include <linux/dma-mapping.h>
2012-07-26 01:20:03 +04:00
# include <linux/memblock.h>
2009-05-14 16:42:28 +04:00
# include <linux/pfn.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/pci.h>
# include <asm/machdep.h>
# include <asm/swiotlb.h>
# include <asm/dma.h>
unsigned int ppc_swiotlb_enable ;
2011-06-24 13:05:24 +04:00
static u64 swiotlb_powerpc_get_required ( struct device * dev )
{
u64 end , mask , max_direct_dma_addr = dev - > archdata . max_direct_dma_addr ;
end = memblock_end_of_DRAM ( ) ;
if ( max_direct_dma_addr & & end > max_direct_dma_addr )
end = max_direct_dma_addr ;
end + = get_dma_offset ( dev ) ;
mask = 1ULL < < ( fls64 ( end ) - 1 ) ;
mask + = mask - 1 ;
return mask ;
}
2009-05-14 16:42:28 +04:00
/*
* At the moment , all platforms that use this code only require
* swiotlb to be used if we ' re operating on HIGHMEM . Since
* we don ' t ever call anything other than map_sg , unmap_sg ,
* map_page , and unmap_page on highmem , use normal dma_ops
* for everything else .
*/
2009-08-04 23:08:25 +04:00
struct dma_map_ops swiotlb_dma_ops = {
2011-12-06 17:14:46 +04:00
. alloc = dma_direct_alloc_coherent ,
. free = dma_direct_free_coherent ,
2012-06-14 15:03:04 +04:00
. mmap = dma_direct_mmap_coherent ,
2009-05-14 16:42:28 +04:00
. map_sg = swiotlb_map_sg_attrs ,
. unmap_sg = swiotlb_unmap_sg_attrs ,
. dma_supported = swiotlb_dma_supported ,
. map_page = swiotlb_map_page ,
. unmap_page = swiotlb_unmap_page ,
2010-05-27 01:44:17 +04:00
. sync_single_for_cpu = swiotlb_sync_single_for_cpu ,
. sync_single_for_device = swiotlb_sync_single_for_device ,
2009-05-14 16:42:28 +04:00
. sync_sg_for_cpu = swiotlb_sync_sg_for_cpu ,
2009-08-04 23:08:27 +04:00
. sync_sg_for_device = swiotlb_sync_sg_for_device ,
. mapping_error = swiotlb_dma_mapping_error ,
2011-06-24 13:05:24 +04:00
. get_required_mask = swiotlb_powerpc_get_required ,
2009-05-14 16:42:28 +04:00
} ;
2009-08-04 23:08:22 +04:00
void pci_dma_dev_setup_swiotlb ( struct pci_dev * pdev )
{
struct pci_controller * hose ;
struct dev_archdata * sd ;
hose = pci_bus_to_host ( pdev - > bus ) ;
sd = & pdev - > dev . archdata ;
sd - > max_direct_dma_addr =
hose - > dma_window_base_cur + hose - > dma_window_size ;
}
2009-05-14 16:42:28 +04:00
static int ppc_swiotlb_bus_notify ( struct notifier_block * nb ,
unsigned long action , void * data )
{
struct device * dev = data ;
2009-08-04 23:08:22 +04:00
struct dev_archdata * sd ;
2009-05-14 16:42:28 +04:00
/* We are only intereted in device addition */
if ( action ! = BUS_NOTIFY_ADD_DEVICE )
return 0 ;
2009-08-04 23:08:22 +04:00
sd = & dev - > archdata ;
sd - > max_direct_dma_addr = 0 ;
2009-05-14 16:42:28 +04:00
/* May need to bounce if the device can't address all of DRAM */
2010-07-12 08:36:09 +04:00
if ( ( dma_get_mask ( dev ) + 1 ) < memblock_end_of_DRAM ( ) )
2009-05-14 16:42:28 +04:00
set_dma_ops ( dev , & swiotlb_dma_ops ) ;
return NOTIFY_DONE ;
}
static struct notifier_block ppc_swiotlb_plat_bus_notifier = {
. notifier_call = ppc_swiotlb_bus_notify ,
. priority = 0 ,
} ;
int __init swiotlb_setup_bus_notifier ( void )
{
bus_register_notifier ( & platform_bus_type ,
& ppc_swiotlb_plat_bus_notifier ) ;
return 0 ;
}
2012-08-03 14:14:10 +04:00
void swiotlb_detect_4g ( void )
{
if ( ( memblock_end_of_DRAM ( ) - 1 ) > 0xffffffff )
ppc_swiotlb_enable = 1 ;
}
static int __init swiotlb_late_init ( void )
{
if ( ppc_swiotlb_enable ) {
swiotlb_print_info ( ) ;
set_pci_dma_ops ( & swiotlb_dma_ops ) ;
ppc_md . pci_dma_dev_setup = pci_dma_dev_setup_swiotlb ;
} else {
swiotlb_free ( ) ;
}
return 0 ;
}
subsys_initcall ( swiotlb_late_init ) ;