2014-06-05 10:17:57 -05:00
/*
* AMD Cryptographic Coprocessor ( CCP ) driver
*
2016-03-01 13:49:04 -06:00
* Copyright ( C ) 2014 , 2016 Advanced Micro Devices , Inc .
2014-06-05 10:17:57 -05:00
*
* Author : Tom Lendacky < thomas . lendacky @ amd . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/ioport.h>
# include <linux/dma-mapping.h>
# include <linux/kthread.h>
# include <linux/sched.h>
# include <linux/interrupt.h>
# include <linux/spinlock.h>
# include <linux/delay.h>
# include <linux/ccp.h>
2014-07-10 10:58:35 -05:00
# include <linux/of.h>
2015-02-03 13:07:29 -06:00
# include <linux/of_address.h>
# include <linux/acpi.h>
2014-06-05 10:17:57 -05:00
# include "ccp-dev.h"
2015-02-03 13:07:29 -06:00
struct ccp_platform {
int coherent ;
} ;
2016-03-01 13:49:15 -06:00
static const struct acpi_device_id ccp_acpi_match [ ] ;
static const struct of_device_id ccp_of_match [ ] ;
static struct ccp_vdata * ccp_get_of_version ( struct platform_device * pdev )
{
# ifdef CONFIG_OF
const struct of_device_id * match ;
match = of_match_node ( ccp_of_match , pdev - > dev . of_node ) ;
if ( match & & match - > data )
return ( struct ccp_vdata * ) match - > data ;
# endif
return 0 ;
}
static struct ccp_vdata * ccp_get_acpi_version ( struct platform_device * pdev )
{
# ifdef CONFIG_ACPI
const struct acpi_device_id * match ;
match = acpi_match_device ( ccp_acpi_match , & pdev - > dev ) ;
if ( match & & match - > driver_data )
return ( struct ccp_vdata * ) match - > driver_data ;
# endif
return 0 ;
}
2014-06-05 10:17:57 -05:00
static int ccp_get_irq ( struct ccp_device * ccp )
{
struct device * dev = ccp - > dev ;
2015-12-23 20:49:01 +08:00
struct platform_device * pdev = to_platform_device ( dev ) ;
2014-06-05 10:17:57 -05:00
int ret ;
ret = platform_get_irq ( pdev , 0 ) ;
if ( ret < 0 )
return ret ;
ccp - > irq = ret ;
2016-03-01 13:49:25 -06:00
ret = request_irq ( ccp - > irq , ccp - > vdata - > perform - > irqhandler , 0 ,
ccp - > name , dev ) ;
2014-06-05 10:17:57 -05:00
if ( ret ) {
dev_notice ( dev , " unable to allocate IRQ (%d) \n " , ret ) ;
return ret ;
}
return 0 ;
}
static int ccp_get_irqs ( struct ccp_device * ccp )
{
struct device * dev = ccp - > dev ;
int ret ;
ret = ccp_get_irq ( ccp ) ;
if ( ! ret )
return 0 ;
/* Couldn't get an interrupt */
dev_notice ( dev , " could not enable interrupts (%d) \n " , ret ) ;
return ret ;
}
static void ccp_free_irqs ( struct ccp_device * ccp )
{
struct device * dev = ccp - > dev ;
free_irq ( ccp - > irq , dev ) ;
}
static struct resource * ccp_find_mmio_area ( struct ccp_device * ccp )
{
struct device * dev = ccp - > dev ;
2015-12-23 20:49:01 +08:00
struct platform_device * pdev = to_platform_device ( dev ) ;
2014-06-05 10:17:57 -05:00
struct resource * ior ;
ior = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ior & & ( resource_size ( ior ) > = 0x800 ) )
return ior ;
return NULL ;
}
static int ccp_platform_probe ( struct platform_device * pdev )
{
struct ccp_device * ccp ;
2015-02-03 13:07:29 -06:00
struct ccp_platform * ccp_platform ;
2014-06-05 10:17:57 -05:00
struct device * dev = & pdev - > dev ;
2015-10-28 15:50:50 -07:00
enum dev_dma_attr attr ;
2014-06-05 10:17:57 -05:00
struct resource * ior ;
int ret ;
ret = - ENOMEM ;
ccp = ccp_alloc_struct ( dev ) ;
if ( ! ccp )
goto e_err ;
2015-02-03 13:07:29 -06:00
ccp_platform = devm_kzalloc ( dev , sizeof ( * ccp_platform ) , GFP_KERNEL ) ;
if ( ! ccp_platform )
goto e_err ;
ccp - > dev_specific = ccp_platform ;
2016-03-01 13:49:15 -06:00
ccp - > vdata = pdev - > dev . of_node ? ccp_get_of_version ( pdev )
: ccp_get_acpi_version ( pdev ) ;
if ( ! ccp - > vdata | | ! ccp - > vdata - > version ) {
ret = - ENODEV ;
dev_err ( dev , " missing driver data \n " ) ;
goto e_err ;
}
2014-06-05 10:17:57 -05:00
ccp - > get_irq = ccp_get_irqs ;
ccp - > free_irq = ccp_free_irqs ;
ior = ccp_find_mmio_area ( ccp ) ;
ccp - > io_map = devm_ioremap_resource ( dev , ior ) ;
if ( IS_ERR ( ccp - > io_map ) ) {
ret = PTR_ERR ( ccp - > io_map ) ;
2015-02-03 13:07:23 -06:00
goto e_err ;
2014-06-05 10:17:57 -05:00
}
ccp - > io_regs = ccp - > io_map ;
2015-10-28 15:50:50 -07:00
attr = device_get_dma_attr ( dev ) ;
if ( attr = = DEV_DMA_NOT_SUPPORTED ) {
dev_err ( dev , " DMA is not supported " ) ;
2015-02-03 13:07:23 -06:00
goto e_err ;
2015-02-03 13:07:17 -06:00
}
2014-06-05 10:17:57 -05:00
2015-10-28 15:50:50 -07:00
ccp_platform - > coherent = ( attr = = DEV_DMA_COHERENT ) ;
2015-02-03 13:07:29 -06:00
if ( ccp_platform - > coherent )
2014-07-10 10:58:35 -05:00
ccp - > axcache = CACHE_WB_NO_ALLOC ;
else
ccp - > axcache = CACHE_NONE ;
2015-10-28 15:50:50 -07:00
ret = dma_set_mask_and_coherent ( dev , DMA_BIT_MASK ( 48 ) ) ;
if ( ret ) {
dev_err ( dev , " dma_set_mask_and_coherent failed (%d) \n " , ret ) ;
goto e_err ;
}
2014-06-05 10:17:57 -05:00
dev_set_drvdata ( dev , ccp ) ;
2016-03-01 13:49:25 -06:00
ret = ccp - > vdata - > perform - > init ( ccp ) ;
2014-06-05 10:17:57 -05:00
if ( ret )
2015-02-03 13:07:23 -06:00
goto e_err ;
2014-06-05 10:17:57 -05:00
dev_notice ( dev , " enabled \n " ) ;
return 0 ;
e_err :
dev_notice ( dev , " initialization failed \n " ) ;
return ret ;
}
static int ccp_platform_remove ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct ccp_device * ccp = dev_get_drvdata ( dev ) ;
2016-03-01 13:49:25 -06:00
ccp - > vdata - > perform - > destroy ( ccp ) ;
2014-06-05 10:17:57 -05:00
dev_notice ( dev , " disabled \n " ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int ccp_platform_suspend ( struct platform_device * pdev ,
pm_message_t state )
{
struct device * dev = & pdev - > dev ;
struct ccp_device * ccp = dev_get_drvdata ( dev ) ;
unsigned long flags ;
unsigned int i ;
spin_lock_irqsave ( & ccp - > cmd_lock , flags ) ;
ccp - > suspending = 1 ;
/* Wake all the queue kthreads to prepare for suspend */
for ( i = 0 ; i < ccp - > cmd_q_count ; i + + )
wake_up_process ( ccp - > cmd_q [ i ] . kthread ) ;
spin_unlock_irqrestore ( & ccp - > cmd_lock , flags ) ;
/* Wait for all queue kthreads to say they're done */
while ( ! ccp_queues_suspended ( ccp ) )
wait_event_interruptible ( ccp - > suspend_queue ,
ccp_queues_suspended ( ccp ) ) ;
return 0 ;
}
static int ccp_platform_resume ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct ccp_device * ccp = dev_get_drvdata ( dev ) ;
unsigned long flags ;
unsigned int i ;
spin_lock_irqsave ( & ccp - > cmd_lock , flags ) ;
ccp - > suspending = 0 ;
/* Wake up all the kthreads */
for ( i = 0 ; i < ccp - > cmd_q_count ; i + + ) {
ccp - > cmd_q [ i ] . suspended = 0 ;
wake_up_process ( ccp - > cmd_q [ i ] . kthread ) ;
}
spin_unlock_irqrestore ( & ccp - > cmd_lock , flags ) ;
return 0 ;
}
# endif
2015-02-03 13:07:29 -06:00
# ifdef CONFIG_ACPI
static const struct acpi_device_id ccp_acpi_match [ ] = {
2016-03-01 13:49:15 -06:00
{ " AMDI0C00 " , ( kernel_ulong_t ) & ccpv3 } ,
2015-02-03 13:07:29 -06:00
{ } ,
} ;
2015-06-30 12:57:14 -05:00
MODULE_DEVICE_TABLE ( acpi , ccp_acpi_match ) ;
2015-02-03 13:07:29 -06:00
# endif
# ifdef CONFIG_OF
static const struct of_device_id ccp_of_match [ ] = {
2016-03-01 13:49:15 -06:00
{ . compatible = " amd,ccp-seattle-v1a " ,
. data = ( const void * ) & ccpv3 } ,
2014-06-05 10:17:57 -05:00
{ } ,
} ;
2015-06-30 12:57:14 -05:00
MODULE_DEVICE_TABLE ( of , ccp_of_match ) ;
2015-02-03 13:07:29 -06:00
# endif
2014-06-05 10:17:57 -05:00
static struct platform_driver ccp_platform_driver = {
. driver = {
2015-10-01 16:32:50 -05:00
. name = " ccp " ,
2015-02-03 13:07:29 -06:00
# ifdef CONFIG_ACPI
. acpi_match_table = ccp_acpi_match ,
# endif
# ifdef CONFIG_OF
. of_match_table = ccp_of_match ,
# endif
2014-06-05 10:17:57 -05:00
} ,
. probe = ccp_platform_probe ,
. remove = ccp_platform_remove ,
# ifdef CONFIG_PM
. suspend = ccp_platform_suspend ,
. resume = ccp_platform_resume ,
# endif
} ;
int ccp_platform_init ( void )
{
return platform_driver_register ( & ccp_platform_driver ) ;
}
void ccp_platform_exit ( void )
{
platform_driver_unregister ( & ccp_platform_driver ) ;
}