2023-03-10 15:19:47 -06:00
// SPDX-License-Identifier: GPL-2.0
/*
* AMD Platform Security Processor ( PSP ) Platform Access interface
*
* Copyright ( C ) 2023 Advanced Micro Devices , Inc .
*
* Author : Mario Limonciello < mario . limonciello @ amd . com >
*
* Some of this code is adapted from drivers / i2c / busses / i2c - designware - amdpsp . c
* developed by Jan Dabros < jsd @ semihalf . com > and Copyright ( C ) 2022 Google Inc .
*
*/
# include <linux/bitfield.h>
# include <linux/errno.h>
# include <linux/iopoll.h>
# include <linux/mutex.h>
# include "platform-access.h"
# define PSP_CMD_TIMEOUT_US (500 * USEC_PER_MSEC)
2023-04-03 13:32:13 -05:00
# define DOORBELL_CMDRESP_STS GENMASK(7, 0)
2023-03-10 15:19:47 -06:00
/* Recovery field should be equal 0 to start sending commands */
static int check_recovery ( u32 __iomem * cmd )
{
return FIELD_GET ( PSP_CMDRESP_RECOVERY , ioread32 ( cmd ) ) ;
}
static int wait_cmd ( u32 __iomem * cmd )
{
u32 tmp , expected ;
/* Expect mbox_cmd to be cleared and ready bit to be set by PSP */
expected = FIELD_PREP ( PSP_CMDRESP_RESP , 1 ) ;
/*
* Check for readiness of PSP mailbox in a tight loop in order to
* process further as soon as command was consumed .
*/
return readl_poll_timeout ( cmd , tmp , ( tmp & expected ) , 0 ,
PSP_CMD_TIMEOUT_US ) ;
}
int psp_check_platform_access_status ( void )
{
struct psp_device * psp = psp_get_master_device ( ) ;
if ( ! psp | | ! psp - > platform_access_data )
return - ENODEV ;
return 0 ;
}
EXPORT_SYMBOL ( psp_check_platform_access_status ) ;
int psp_send_platform_access_msg ( enum psp_platform_access_msg msg ,
struct psp_request * req )
{
struct psp_device * psp = psp_get_master_device ( ) ;
u32 __iomem * cmd , * lo , * hi ;
struct psp_platform_access_device * pa_dev ;
phys_addr_t req_addr ;
u32 cmd_reg ;
int ret ;
if ( ! psp | | ! psp - > platform_access_data )
return - ENODEV ;
pa_dev = psp - > platform_access_data ;
2023-05-18 22:24:12 -05:00
if ( ! pa_dev - > vdata - > cmdresp_reg | | ! pa_dev - > vdata - > cmdbuff_addr_lo_reg | |
! pa_dev - > vdata - > cmdbuff_addr_hi_reg )
return - ENODEV ;
2023-03-10 15:19:47 -06:00
cmd = psp - > io_regs + pa_dev - > vdata - > cmdresp_reg ;
lo = psp - > io_regs + pa_dev - > vdata - > cmdbuff_addr_lo_reg ;
hi = psp - > io_regs + pa_dev - > vdata - > cmdbuff_addr_hi_reg ;
mutex_lock ( & pa_dev - > mailbox_mutex ) ;
if ( check_recovery ( cmd ) ) {
dev_dbg ( psp - > dev , " platform mailbox is in recovery \n " ) ;
ret = - EBUSY ;
goto unlock ;
}
if ( wait_cmd ( cmd ) ) {
dev_dbg ( psp - > dev , " platform mailbox is not done processing command \n " ) ;
ret = - EBUSY ;
goto unlock ;
}
/*
* Fill mailbox with address of command - response buffer , which will be
* used for sending i2c requests as well as reading status returned by
* PSP . Use physical address of buffer , since PSP will map this region .
*/
req_addr = __psp_pa ( req ) ;
iowrite32 ( lower_32_bits ( req_addr ) , lo ) ;
iowrite32 ( upper_32_bits ( req_addr ) , hi ) ;
print_hex_dump_debug ( " ->psp " , DUMP_PREFIX_OFFSET , 16 , 2 , req ,
req - > header . payload_size , false ) ;
/* Write command register to trigger processing */
cmd_reg = FIELD_PREP ( PSP_CMDRESP_CMD , msg ) ;
iowrite32 ( cmd_reg , cmd ) ;
if ( wait_cmd ( cmd ) ) {
ret = - ETIMEDOUT ;
goto unlock ;
}
/* Ensure it was triggered by this driver */
if ( ioread32 ( lo ) ! = lower_32_bits ( req_addr ) | |
ioread32 ( hi ) ! = upper_32_bits ( req_addr ) ) {
ret = - EBUSY ;
goto unlock ;
}
/* Store the status in request header for caller to investigate */
cmd_reg = ioread32 ( cmd ) ;
req - > header . status = FIELD_GET ( PSP_CMDRESP_STS , cmd_reg ) ;
if ( req - > header . status ) {
ret = - EIO ;
goto unlock ;
}
print_hex_dump_debug ( " <-psp " , DUMP_PREFIX_OFFSET , 16 , 2 , req ,
req - > header . payload_size , false ) ;
ret = 0 ;
unlock :
mutex_unlock ( & pa_dev - > mailbox_mutex ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( psp_send_platform_access_msg ) ;
2023-04-03 13:32:12 -05:00
int psp_ring_platform_doorbell ( int msg , u32 * result )
2023-03-10 15:19:50 -06:00
{
struct psp_device * psp = psp_get_master_device ( ) ;
struct psp_platform_access_device * pa_dev ;
u32 __iomem * button , * cmd ;
int ret , val ;
if ( ! psp | | ! psp - > platform_access_data )
return - ENODEV ;
pa_dev = psp - > platform_access_data ;
button = psp - > io_regs + pa_dev - > vdata - > doorbell_button_reg ;
cmd = psp - > io_regs + pa_dev - > vdata - > doorbell_cmd_reg ;
mutex_lock ( & pa_dev - > doorbell_mutex ) ;
if ( wait_cmd ( cmd ) ) {
2023-04-03 13:32:11 -05:00
dev_err ( psp - > dev , " doorbell command not done processing \n " ) ;
2023-03-10 15:19:50 -06:00
ret = - EBUSY ;
goto unlock ;
}
2023-04-03 13:32:13 -05:00
iowrite32 ( FIELD_PREP ( DOORBELL_CMDRESP_STS , msg ) , cmd ) ;
2023-03-10 15:19:50 -06:00
iowrite32 ( PSP_DRBL_RING , button ) ;
if ( wait_cmd ( cmd ) ) {
ret = - ETIMEDOUT ;
goto unlock ;
}
2023-04-03 13:32:13 -05:00
val = FIELD_GET ( DOORBELL_CMDRESP_STS , ioread32 ( cmd ) ) ;
2023-03-10 15:19:50 -06:00
if ( val ) {
2023-04-03 13:32:12 -05:00
if ( result )
* result = val ;
2023-03-10 15:19:50 -06:00
ret = - EIO ;
goto unlock ;
}
ret = 0 ;
unlock :
mutex_unlock ( & pa_dev - > doorbell_mutex ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( psp_ring_platform_doorbell ) ;
2023-03-10 15:19:47 -06:00
void platform_access_dev_destroy ( struct psp_device * psp )
{
struct psp_platform_access_device * pa_dev = psp - > platform_access_data ;
if ( ! pa_dev )
return ;
mutex_destroy ( & pa_dev - > mailbox_mutex ) ;
2023-03-10 15:19:50 -06:00
mutex_destroy ( & pa_dev - > doorbell_mutex ) ;
2023-03-10 15:19:47 -06:00
psp - > platform_access_data = NULL ;
}
int platform_access_dev_init ( struct psp_device * psp )
{
struct device * dev = psp - > dev ;
struct psp_platform_access_device * pa_dev ;
pa_dev = devm_kzalloc ( dev , sizeof ( * pa_dev ) , GFP_KERNEL ) ;
if ( ! pa_dev )
return - ENOMEM ;
psp - > platform_access_data = pa_dev ;
pa_dev - > psp = psp ;
pa_dev - > dev = dev ;
pa_dev - > vdata = ( struct platform_access_vdata * ) psp - > vdata - > platform_access ;
mutex_init ( & pa_dev - > mailbox_mutex ) ;
2023-03-10 15:19:50 -06:00
mutex_init ( & pa_dev - > doorbell_mutex ) ;
2023-03-10 15:19:47 -06:00
dev_dbg ( dev , " platform access enabled \n " ) ;
return 0 ;
}