2009-05-14 12:42:28 +00:00
/*
* Contains routines needed to support swiotlb for ppc .
*
* Copyright ( C ) 2009 Becky Bruce , Freescale Semiconductor
*
* 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>
# 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>
# include <asm/abs_addr.h>
int swiotlb __read_mostly ;
unsigned int ppc_swiotlb_enable ;
/*
* Determine if an address is reachable by a pci device , or if we must bounce .
*/
static int
swiotlb_pci_addr_needs_map ( struct device * hwdev , dma_addr_t addr , size_t size )
{
dma_addr_t max ;
struct pci_controller * hose ;
struct pci_dev * pdev = to_pci_dev ( hwdev ) ;
hose = pci_bus_to_host ( pdev - > bus ) ;
max = hose - > dma_window_base_cur + hose - > dma_window_size ;
/* check that we're within mapped pci window space */
if ( ( addr + size > max ) | ( addr < hose - > dma_window_base_cur ) )
return 1 ;
2009-07-10 10:04:59 +09:00
return 0 ;
2009-05-14 12:42:28 +00: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 .
*/
struct dma_mapping_ops swiotlb_dma_ops = {
. alloc_coherent = dma_direct_alloc_coherent ,
. free_coherent = dma_direct_free_coherent ,
. 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 ,
. sync_single_range_for_cpu = swiotlb_sync_single_range_for_cpu ,
. sync_single_range_for_device = swiotlb_sync_single_range_for_device ,
. sync_sg_for_cpu = swiotlb_sync_sg_for_cpu ,
. sync_sg_for_device = swiotlb_sync_sg_for_device
} ;
struct dma_mapping_ops swiotlb_pci_dma_ops = {
. alloc_coherent = dma_direct_alloc_coherent ,
. free_coherent = dma_direct_free_coherent ,
. 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 ,
. addr_needs_map = swiotlb_pci_addr_needs_map ,
. sync_single_range_for_cpu = swiotlb_sync_single_range_for_cpu ,
. sync_single_range_for_device = swiotlb_sync_single_range_for_device ,
. sync_sg_for_cpu = swiotlb_sync_sg_for_cpu ,
. sync_sg_for_device = swiotlb_sync_sg_for_device
} ;
static int ppc_swiotlb_bus_notify ( struct notifier_block * nb ,
unsigned long action , void * data )
{
struct device * dev = data ;
/* We are only intereted in device addition */
if ( action ! = BUS_NOTIFY_ADD_DEVICE )
return 0 ;
/* May need to bounce if the device can't address all of DRAM */
if ( dma_get_mask ( dev ) < lmb_end_of_DRAM ( ) )
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 ,
} ;
static struct notifier_block ppc_swiotlb_of_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 ) ;
bus_register_notifier ( & of_platform_bus_type ,
& ppc_swiotlb_of_bus_notifier ) ;
return 0 ;
}