2013-03-22 18:34:01 +04:00
/*
* Tegra host1x driver
*
* Copyright ( c ) 2010 - 2013 , NVIDIA Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/clk.h>
2016-02-26 12:06:52 +03:00
# include <linux/dma-mapping.h>
2017-03-21 10:54:21 +03:00
# include <linux/io.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/of.h>
# include <linux/slab.h>
2013-03-22 18:34:01 +04:00
# define CREATE_TRACE_POINTS
# include <trace/events/host1x.h>
2016-12-14 14:16:14 +03:00
# undef CREATE_TRACE_POINTS
2013-03-22 18:34:01 +04:00
2018-08-30 03:12:47 +03:00
# if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
# include <asm/dma-iommu.h>
# endif
2013-10-14 16:43:22 +04:00
# include "bus.h"
2013-03-22 18:34:03 +04:00
# include "channel.h"
2013-03-22 18:34:04 +04:00
# include "debug.h"
2017-03-21 10:54:21 +03:00
# include "dev.h"
# include "intr.h"
2013-03-22 18:34:01 +04:00
# include "hw/host1x01.h"
2013-09-30 16:17:39 +04:00
# include "hw/host1x02.h"
2013-11-15 17:58:05 +04:00
# include "hw/host1x04.h"
2015-03-23 12:46:28 +03:00
# include "hw/host1x05.h"
2017-09-05 11:43:05 +03:00
# include "hw/host1x06.h"
2018-01-25 15:10:44 +03:00
# include "hw/host1x07.h"
2017-09-05 11:43:05 +03:00
void host1x_hypervisor_writel ( struct host1x * host1x , u32 v , u32 r )
{
writel ( v , host1x - > hv_regs + r ) ;
}
u32 host1x_hypervisor_readl ( struct host1x * host1x , u32 r )
{
return readl ( host1x - > hv_regs + r ) ;
}
2013-03-22 18:34:01 +04:00
void host1x_sync_writel ( struct host1x * host1x , u32 v , u32 r )
{
void __iomem * sync_regs = host1x - > regs + host1x - > info - > sync_offset ;
writel ( v , sync_regs + r ) ;
}
u32 host1x_sync_readl ( struct host1x * host1x , u32 r )
{
void __iomem * sync_regs = host1x - > regs + host1x - > info - > sync_offset ;
return readl ( sync_regs + r ) ;
}
2013-03-22 18:34:03 +04:00
void host1x_ch_writel ( struct host1x_channel * ch , u32 v , u32 r )
{
writel ( v , ch - > regs + r ) ;
}
u32 host1x_ch_readl ( struct host1x_channel * ch , u32 r )
{
return readl ( ch - > regs + r ) ;
}
2013-03-22 18:34:01 +04:00
static const struct host1x_info host1x01_info = {
2016-06-23 12:35:50 +03:00
. nb_channels = 8 ,
. nb_pts = 32 ,
. nb_mlocks = 16 ,
. nb_bases = 8 ,
. init = host1x01_init ,
. sync_offset = 0x3000 ,
. dma_mask = DMA_BIT_MASK ( 32 ) ,
2013-03-22 18:34:01 +04:00
} ;
2013-09-30 16:17:39 +04:00
static const struct host1x_info host1x02_info = {
. nb_channels = 9 ,
. nb_pts = 32 ,
. nb_mlocks = 16 ,
. nb_bases = 12 ,
. init = host1x02_init ,
. sync_offset = 0x3000 ,
2016-02-26 12:06:52 +03:00
. dma_mask = DMA_BIT_MASK ( 32 ) ,
2013-09-30 16:17:39 +04:00
} ;
2013-11-15 17:58:05 +04:00
static const struct host1x_info host1x04_info = {
. nb_channels = 12 ,
. nb_pts = 192 ,
. nb_mlocks = 16 ,
. nb_bases = 64 ,
. init = host1x04_init ,
. sync_offset = 0x2100 ,
2016-02-26 12:06:52 +03:00
. dma_mask = DMA_BIT_MASK ( 34 ) ,
2013-11-15 17:58:05 +04:00
} ;
2015-03-23 12:46:28 +03:00
static const struct host1x_info host1x05_info = {
. nb_channels = 14 ,
. nb_pts = 192 ,
. nb_mlocks = 16 ,
. nb_bases = 64 ,
. init = host1x05_init ,
. sync_offset = 0x2100 ,
2016-02-26 12:06:52 +03:00
. dma_mask = DMA_BIT_MASK ( 34 ) ,
2015-03-23 12:46:28 +03:00
} ;
2019-02-01 16:28:22 +03:00
static const struct host1x_sid_entry tegra186_sid_table [ ] = {
{
/* VIC */
. base = 0x1af0 ,
. offset = 0x30 ,
. limit = 0x34
} ,
} ;
2017-09-05 11:43:05 +03:00
static const struct host1x_info host1x06_info = {
. nb_channels = 63 ,
. nb_pts = 576 ,
. nb_mlocks = 24 ,
. nb_bases = 16 ,
. init = host1x06_init ,
. sync_offset = 0x0 ,
. dma_mask = DMA_BIT_MASK ( 34 ) ,
. has_hypervisor = true ,
2019-02-01 16:28:22 +03:00
. num_sid_entries = ARRAY_SIZE ( tegra186_sid_table ) ,
. sid_table = tegra186_sid_table ,
} ;
static const struct host1x_sid_entry tegra194_sid_table [ ] = {
{
/* VIC */
. base = 0x1af0 ,
. offset = 0x30 ,
. limit = 0x34
} ,
2017-09-05 11:43:05 +03:00
} ;
2018-01-25 15:10:44 +03:00
static const struct host1x_info host1x07_info = {
. nb_channels = 63 ,
. nb_pts = 704 ,
. nb_mlocks = 32 ,
. nb_bases = 0 ,
. init = host1x07_init ,
. sync_offset = 0x0 ,
. dma_mask = DMA_BIT_MASK ( 40 ) ,
. has_hypervisor = true ,
2019-02-01 16:28:22 +03:00
. num_sid_entries = ARRAY_SIZE ( tegra194_sid_table ) ,
. sid_table = tegra194_sid_table ,
2018-01-25 15:10:44 +03:00
} ;
2016-06-23 12:33:31 +03:00
static const struct of_device_id host1x_of_match [ ] = {
2018-01-25 15:10:44 +03:00
{ . compatible = " nvidia,tegra194-host1x " , . data = & host1x07_info , } ,
2017-09-05 11:43:05 +03:00
{ . compatible = " nvidia,tegra186-host1x " , . data = & host1x06_info , } ,
2015-03-23 12:46:28 +03:00
{ . compatible = " nvidia,tegra210-host1x " , . data = & host1x05_info , } ,
2013-11-15 17:58:05 +04:00
{ . compatible = " nvidia,tegra124-host1x " , . data = & host1x04_info , } ,
2013-09-30 16:17:39 +04:00
{ . compatible = " nvidia,tegra114-host1x " , . data = & host1x02_info , } ,
2013-03-22 18:34:01 +04:00
{ . compatible = " nvidia,tegra30-host1x " , . data = & host1x01_info , } ,
{ . compatible = " nvidia,tegra20-host1x " , . data = & host1x01_info , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , host1x_of_match ) ;
2019-02-01 16:28:22 +03:00
static void host1x_setup_sid_table ( struct host1x * host )
{
const struct host1x_info * info = host - > info ;
unsigned int i ;
for ( i = 0 ; i < info - > num_sid_entries ; i + + ) {
const struct host1x_sid_entry * entry = & info - > sid_table [ i ] ;
host1x_hypervisor_writel ( host , entry - > offset , entry - > base ) ;
host1x_hypervisor_writel ( host , entry - > limit , entry - > base + 4 ) ;
}
}
2013-03-22 18:34:01 +04:00
static int host1x_probe ( struct platform_device * pdev )
{
struct host1x * host ;
2017-09-05 11:43:05 +03:00
struct resource * regs , * hv_regs = NULL ;
2013-03-22 18:34:01 +04:00
int syncpt_irq ;
int err ;
2017-08-21 19:08:42 +03:00
host = devm_kzalloc ( & pdev - > dev , sizeof ( * host ) , GFP_KERNEL ) ;
if ( ! host )
return - ENOMEM ;
2013-03-22 18:34:01 +04:00
2017-08-21 19:08:42 +03:00
host - > info = of_device_get_match_data ( & pdev - > dev ) ;
2013-03-22 18:34:01 +04:00
2017-09-05 11:43:05 +03:00
if ( host - > info - > has_hypervisor ) {
regs = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " vm " ) ;
if ( ! regs ) {
dev_err ( & pdev - > dev , " failed to get vm registers \n " ) ;
return - ENXIO ;
}
hv_regs = platform_get_resource_byname ( pdev , IORESOURCE_MEM ,
" hypervisor " ) ;
if ( ! hv_regs ) {
dev_err ( & pdev - > dev ,
" failed to get hypervisor registers \n " ) ;
return - ENXIO ;
}
} else {
regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! regs ) {
dev_err ( & pdev - > dev , " failed to get registers \n " ) ;
return - ENXIO ;
}
2013-03-22 18:34:01 +04:00
}
syncpt_irq = platform_get_irq ( pdev , 0 ) ;
if ( syncpt_irq < 0 ) {
2017-08-08 08:08:06 +03:00
dev_err ( & pdev - > dev , " failed to get IRQ: %d \n " , syncpt_irq ) ;
return syncpt_irq ;
2013-03-22 18:34:01 +04:00
}
2013-10-14 16:43:22 +04:00
mutex_init ( & host - > devices_lock ) ;
INIT_LIST_HEAD ( & host - > devices ) ;
INIT_LIST_HEAD ( & host - > list ) ;
2013-03-22 18:34:01 +04:00
host - > dev = & pdev - > dev ;
/* set common host1x device data */
platform_set_drvdata ( pdev , host ) ;
host - > regs = devm_ioremap_resource ( & pdev - > dev , regs ) ;
if ( IS_ERR ( host - > regs ) )
return PTR_ERR ( host - > regs ) ;
2017-09-05 11:43:05 +03:00
if ( host - > info - > has_hypervisor ) {
host - > hv_regs = devm_ioremap_resource ( & pdev - > dev , hv_regs ) ;
if ( IS_ERR ( host - > hv_regs ) )
return PTR_ERR ( host - > hv_regs ) ;
}
2016-02-26 12:06:52 +03:00
dma_set_mask_and_coherent ( host - > dev , host - > info - > dma_mask ) ;
2013-03-22 18:34:01 +04:00
if ( host - > info - > init ) {
err = host - > info - > init ( host ) ;
if ( err )
return err ;
}
host - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( host - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get clock \n " ) ;
err = PTR_ERR ( host - > clk ) ;
return err ;
}
2017-03-21 10:54:22 +03:00
host - > rst = devm_reset_control_get ( & pdev - > dev , " host1x " ) ;
if ( IS_ERR ( host - > rst ) ) {
2017-04-10 23:29:22 +03:00
err = PTR_ERR ( host - > rst ) ;
2017-03-21 10:54:22 +03:00
dev_err ( & pdev - > dev , " failed to get reset: %d \n " , err ) ;
return err ;
}
2018-08-30 03:12:47 +03:00
# if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
if ( host - > dev - > archdata . mapping ) {
struct dma_iommu_mapping * mapping =
to_dma_iommu_mapping ( host - > dev ) ;
arm_iommu_detach_device ( host - > dev ) ;
arm_iommu_release_mapping ( mapping ) ;
}
# endif
2018-05-19 04:07:10 +03:00
if ( IS_ENABLED ( CONFIG_TEGRA_HOST1X_FIREWALL ) )
goto skip_iommu ;
2017-11-14 18:11:28 +03:00
host - > group = iommu_group_get ( & pdev - > dev ) ;
if ( host - > group ) {
2016-12-14 14:16:14 +03:00
struct iommu_domain_geometry * geometry ;
unsigned long order ;
2018-05-14 12:14:00 +03:00
err = iova_cache_get ( ) ;
if ( err < 0 )
goto put_group ;
2016-12-14 14:16:14 +03:00
host - > domain = iommu_domain_alloc ( & platform_bus_type ) ;
2017-11-14 18:11:28 +03:00
if ( ! host - > domain ) {
err = - ENOMEM ;
2018-05-14 12:14:00 +03:00
goto put_cache ;
2017-11-14 18:11:28 +03:00
}
2016-12-14 14:16:14 +03:00
2017-11-14 18:11:28 +03:00
err = iommu_attach_group ( host - > domain , host - > group ) ;
2017-07-31 12:18:44 +03:00
if ( err ) {
if ( err = = - ENODEV ) {
iommu_domain_free ( host - > domain ) ;
host - > domain = NULL ;
2018-05-14 12:14:00 +03:00
iova_cache_put ( ) ;
2017-11-14 18:11:28 +03:00
iommu_group_put ( host - > group ) ;
host - > group = NULL ;
2017-07-31 12:18:44 +03:00
goto skip_iommu ;
}
2016-12-14 14:16:14 +03:00
goto fail_free_domain ;
2017-07-10 22:33:05 +03:00
}
2016-12-14 14:16:14 +03:00
geometry = & host - > domain - > geometry ;
order = __ffs ( host - > domain - > pgsize_bitmap ) ;
init_iova_domain ( & host - > iova , 1UL < < order ,
2017-09-21 18:52:45 +03:00
geometry - > aperture_start > > order ) ;
2016-12-14 14:16:14 +03:00
host - > iova_end = geometry - > aperture_end ;
}
2017-07-10 22:33:05 +03:00
skip_iommu :
2017-06-15 02:18:42 +03:00
err = host1x_channel_list_init ( & host - > channel_list ,
host - > info - > nb_channels ) ;
2013-03-22 18:34:03 +04:00
if ( err ) {
dev_err ( & pdev - > dev , " failed to initialize channel list \n " ) ;
2016-12-14 14:16:14 +03:00
goto fail_detach_device ;
2013-03-22 18:34:03 +04:00
}
2013-03-22 18:34:01 +04:00
err = clk_prepare_enable ( host - > clk ) ;
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to enable clock \n " ) ;
2017-06-15 02:18:42 +03:00
goto fail_free_channels ;
2013-03-22 18:34:01 +04:00
}
2017-03-21 10:54:22 +03:00
err = reset_control_deassert ( host - > rst ) ;
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to deassert reset: %d \n " , err ) ;
goto fail_unprepare_disable ;
}
2013-03-22 18:34:01 +04:00
err = host1x_syncpt_init ( host ) ;
if ( err ) {
dev_err ( & pdev - > dev , " failed to initialize syncpts \n " ) ;
2017-03-21 10:54:22 +03:00
goto fail_reset_assert ;
2013-03-22 18:34:01 +04:00
}
2013-03-22 18:34:02 +04:00
err = host1x_intr_init ( host , syncpt_irq ) ;
if ( err ) {
dev_err ( & pdev - > dev , " failed to initialize interrupts \n " ) ;
goto fail_deinit_syncpt ;
}
2013-03-22 18:34:04 +04:00
host1x_debug_init ( host ) ;
2019-02-01 16:28:22 +03:00
if ( host - > info - > has_hypervisor )
host1x_setup_sid_table ( host ) ;
2013-10-14 16:43:22 +04:00
err = host1x_register ( host ) ;
if ( err < 0 )
goto fail_deinit_intr ;
2013-03-22 18:34:07 +04:00
2013-03-22 18:34:01 +04:00
return 0 ;
2013-03-22 18:34:02 +04:00
2013-10-14 16:43:22 +04:00
fail_deinit_intr :
host1x_intr_deinit ( host ) ;
2013-03-22 18:34:02 +04:00
fail_deinit_syncpt :
host1x_syncpt_deinit ( host ) ;
2017-03-21 10:54:22 +03:00
fail_reset_assert :
reset_control_assert ( host - > rst ) ;
2013-10-21 09:37:31 +04:00
fail_unprepare_disable :
clk_disable_unprepare ( host - > clk ) ;
2017-06-15 02:18:42 +03:00
fail_free_channels :
host1x_channel_list_free ( & host - > channel_list ) ;
2016-12-14 14:16:14 +03:00
fail_detach_device :
2017-11-14 18:11:28 +03:00
if ( host - > group & & host - > domain ) {
2016-12-14 14:16:14 +03:00
put_iova_domain ( & host - > iova ) ;
2017-11-14 18:11:28 +03:00
iommu_detach_group ( host - > domain , host - > group ) ;
2016-12-14 14:16:14 +03:00
}
fail_free_domain :
if ( host - > domain )
iommu_domain_free ( host - > domain ) ;
2018-05-14 12:14:00 +03:00
put_cache :
if ( host - > group )
iova_cache_put ( ) ;
2017-11-14 18:11:28 +03:00
put_group :
iommu_group_put ( host - > group ) ;
2016-12-14 14:16:14 +03:00
2013-03-22 18:34:02 +04:00
return err ;
2013-03-22 18:34:01 +04:00
}
2013-09-25 20:33:31 +04:00
static int host1x_remove ( struct platform_device * pdev )
2013-03-22 18:34:01 +04:00
{
struct host1x * host = platform_get_drvdata ( pdev ) ;
2013-10-14 16:43:22 +04:00
host1x_unregister ( host ) ;
2013-03-22 18:34:02 +04:00
host1x_intr_deinit ( host ) ;
2013-03-22 18:34:01 +04:00
host1x_syncpt_deinit ( host ) ;
2017-03-21 10:54:22 +03:00
reset_control_assert ( host - > rst ) ;
2013-03-22 18:34:01 +04:00
clk_disable_unprepare ( host - > clk ) ;
2016-12-14 14:16:14 +03:00
if ( host - > domain ) {
put_iova_domain ( & host - > iova ) ;
2017-11-14 18:11:28 +03:00
iommu_detach_group ( host - > domain , host - > group ) ;
2016-12-14 14:16:14 +03:00
iommu_domain_free ( host - > domain ) ;
2018-05-14 12:14:00 +03:00
iova_cache_put ( ) ;
2017-11-14 18:11:28 +03:00
iommu_group_put ( host - > group ) ;
2016-12-14 14:16:14 +03:00
}
2013-03-22 18:34:01 +04:00
return 0 ;
}
2013-03-22 18:34:07 +04:00
static struct platform_driver tegra_host1x_driver = {
2013-03-22 18:34:01 +04:00
. driver = {
. name = " tegra-host1x " ,
. of_match_table = host1x_of_match ,
} ,
2013-09-25 20:33:31 +04:00
. probe = host1x_probe ,
. remove = host1x_remove ,
2013-03-22 18:34:01 +04:00
} ;
2015-12-02 19:24:20 +03:00
static struct platform_driver * const drivers [ ] = {
& tegra_host1x_driver ,
& tegra_mipi_driver ,
} ;
2013-03-22 18:34:07 +04:00
static int __init tegra_host1x_init ( void )
{
int err ;
2014-12-18 17:29:14 +03:00
err = bus_register ( & host1x_bus_type ) ;
2013-03-22 18:34:07 +04:00
if ( err < 0 )
return err ;
2015-12-02 19:24:20 +03:00
err = platform_register_drivers ( drivers , ARRAY_SIZE ( drivers ) ) ;
2013-09-02 11:48:53 +04:00
if ( err < 0 )
2015-12-02 19:24:20 +03:00
bus_unregister ( & host1x_bus_type ) ;
2013-03-22 18:34:07 +04:00
2013-09-02 11:48:53 +04:00
return err ;
2013-03-22 18:34:07 +04:00
}
module_init ( tegra_host1x_init ) ;
static void __exit tegra_host1x_exit ( void )
{
2015-12-02 19:24:20 +03:00
platform_unregister_drivers ( drivers , ARRAY_SIZE ( drivers ) ) ;
2014-12-18 17:29:14 +03:00
bus_unregister ( & host1x_bus_type ) ;
2013-03-22 18:34:07 +04:00
}
module_exit ( tegra_host1x_exit ) ;
2013-03-22 18:34:01 +04:00
2013-03-22 18:34:07 +04:00
MODULE_AUTHOR ( " Thierry Reding <thierry.reding@avionic-design.de> " ) ;
2013-03-22 18:34:01 +04:00
MODULE_AUTHOR ( " Terje Bergstrom <tbergstrom@nvidia.com> " ) ;
MODULE_DESCRIPTION ( " Host1x driver for Tegra products " ) ;
MODULE_LICENSE ( " GPL " ) ;