2018-01-17 19:36:44 +03:00
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2016-10-26 19:20:28 +03:00
/*
2016-10-26 19:20:33 +03:00
* Copyright 2013 - 2016 Freescale Semiconductor Inc .
2015-03-06 04:29:09 +03:00
*
* I / O services to send MC commands to the MC hardware
*
*/
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/ioport.h>
# include <linux/device.h>
2016-08-24 01:14:18 +03:00
# include <linux/io.h>
2017-07-19 14:42:31 +03:00
# include <linux/io-64-nonatomic-hi-lo.h>
2018-02-05 17:07:42 +03:00
# include <linux/fsl/mc.h>
2016-08-24 01:14:18 +03:00
2018-01-16 16:19:04 +03:00
# include "fsl-mc-private.h"
2015-03-06 04:29:09 +03:00
2021-06-17 14:04:54 +03:00
/*
2015-10-17 23:33:12 +03:00
* Timeout in milliseconds to wait for the completion of an MC command
2015-03-06 04:29:09 +03:00
*/
2015-10-17 23:33:12 +03:00
# define MC_CMD_COMPLETION_TIMEOUT_MS 500
2015-03-06 04:29:09 +03:00
/*
* usleep_range ( ) min and max values used to throttle down polling
* iterations while waiting for MC command completion
*/
# define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10
# define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500
2018-03-15 20:05:31 +03:00
static enum mc_cmd_status mc_cmd_hdr_read_status ( struct fsl_mc_command * cmd )
2016-06-23 00:40:52 +03:00
{
struct mc_cmd_header * hdr = ( struct mc_cmd_header * ) & cmd - > header ;
return ( enum mc_cmd_status ) hdr - > status ;
}
2021-01-14 20:07:49 +03:00
u16 mc_cmd_hdr_read_cmdid ( struct fsl_mc_command * cmd )
2016-06-23 00:40:52 +03:00
{
struct mc_cmd_header * hdr = ( struct mc_cmd_header * ) & cmd - > header ;
u16 cmd_id = le16_to_cpu ( hdr - > cmd_id ) ;
2016-10-26 19:20:34 +03:00
return cmd_id ;
2016-06-23 00:40:52 +03:00
}
2015-03-06 04:29:09 +03:00
static int mc_status_to_error ( enum mc_cmd_status status )
{
static const int mc_status_to_error_map [ ] = {
[ MC_CMD_STATUS_OK ] = 0 ,
[ MC_CMD_STATUS_AUTH_ERR ] = - EACCES ,
[ MC_CMD_STATUS_NO_PRIVILEGE ] = - EPERM ,
[ MC_CMD_STATUS_DMA_ERR ] = - EIO ,
[ MC_CMD_STATUS_CONFIG_ERR ] = - ENXIO ,
[ MC_CMD_STATUS_TIMEOUT ] = - ETIMEDOUT ,
[ MC_CMD_STATUS_NO_RESOURCE ] = - ENAVAIL ,
[ MC_CMD_STATUS_NO_MEMORY ] = - ENOMEM ,
[ MC_CMD_STATUS_BUSY ] = - EBUSY ,
[ MC_CMD_STATUS_UNSUPPORTED_OP ] = - ENOTSUPP ,
[ MC_CMD_STATUS_INVALID_STATE ] = - ENODEV ,
} ;
2017-11-17 16:38:32 +03:00
if ( ( u32 ) status > = ARRAY_SIZE ( mc_status_to_error_map ) )
2015-03-06 04:29:09 +03:00
return - EINVAL ;
return mc_status_to_error_map [ status ] ;
}
static const char * mc_status_to_string ( enum mc_cmd_status status )
{
static const char * const status_strings [ ] = {
[ MC_CMD_STATUS_OK ] = " Command completed successfully " ,
[ MC_CMD_STATUS_READY ] = " Command ready to be processed " ,
[ MC_CMD_STATUS_AUTH_ERR ] = " Authentication error " ,
[ MC_CMD_STATUS_NO_PRIVILEGE ] = " No privilege " ,
[ MC_CMD_STATUS_DMA_ERR ] = " DMA or I/O error " ,
[ MC_CMD_STATUS_CONFIG_ERR ] = " Configuration error " ,
[ MC_CMD_STATUS_TIMEOUT ] = " Operation timed out " ,
[ MC_CMD_STATUS_NO_RESOURCE ] = " No resources " ,
[ MC_CMD_STATUS_NO_MEMORY ] = " No memory available " ,
[ MC_CMD_STATUS_BUSY ] = " Device is busy " ,
[ MC_CMD_STATUS_UNSUPPORTED_OP ] = " Unsupported operation " ,
[ MC_CMD_STATUS_INVALID_STATE ] = " Invalid state "
} ;
if ( ( unsigned int ) status > = ARRAY_SIZE ( status_strings ) )
return " Unknown MC error " ;
return status_strings [ status ] ;
}
/**
* mc_write_command - writes a command to a Management Complex ( MC ) portal
*
* @ portal : pointer to an MC portal
* @ cmd : pointer to a filled command
*/
2018-03-15 20:05:31 +03:00
static inline void mc_write_command ( struct fsl_mc_command __iomem * portal ,
struct fsl_mc_command * cmd )
2015-03-06 04:29:09 +03:00
{
int i ;
/* copy command parameters into the portal */
for ( i = 0 ; i < MC_CMD_NUM_OF_PARAMS ; i + + )
2017-07-19 14:42:30 +03:00
/*
* Data is already in the expected LE byte - order . Do an
* extra LE - > CPU conversion so that the CPU - > LE done in
* the device io write api puts it back in the right order .
*/
writeq_relaxed ( le64_to_cpu ( cmd - > params [ i ] ) , & portal - > params [ i ] ) ;
2015-03-06 04:29:09 +03:00
/* submit the command by writing the header */
2017-07-19 14:42:30 +03:00
writeq ( le64_to_cpu ( cmd - > header ) , & portal - > header ) ;
2015-03-06 04:29:09 +03:00
}
/**
* mc_read_response - reads the response for the last MC command from a
* Management Complex ( MC ) portal
*
* @ portal : pointer to an MC portal
* @ resp : pointer to command response buffer
*
* Returns MC_CMD_STATUS_OK on Success ; Error code otherwise .
*/
2018-03-15 20:05:31 +03:00
static inline enum mc_cmd_status mc_read_response ( struct fsl_mc_command __iomem
* portal ,
struct fsl_mc_command * resp )
2015-03-06 04:29:09 +03:00
{
int i ;
enum mc_cmd_status status ;
/* Copy command response header from MC portal: */
2017-07-19 14:42:30 +03:00
resp - > header = cpu_to_le64 ( readq_relaxed ( & portal - > header ) ) ;
2016-06-23 00:40:52 +03:00
status = mc_cmd_hdr_read_status ( resp ) ;
2015-03-06 04:29:09 +03:00
if ( status ! = MC_CMD_STATUS_OK )
return status ;
/* Copy command response data from MC portal: */
for ( i = 0 ; i < MC_CMD_NUM_OF_PARAMS ; i + + )
2017-07-19 14:42:30 +03:00
/*
* Data is expected to be in LE byte - order . Do an
* extra CPU - > LE to revert the LE - > CPU done in
* the device io read api .
*/
resp - > params [ i ] =
cpu_to_le64 ( readq_relaxed ( & portal - > params [ i ] ) ) ;
2015-03-06 04:29:09 +03:00
return status ;
}
/**
2021-06-17 14:04:54 +03:00
* mc_polling_wait_preemptible ( ) - Waits for the completion of an MC
* command doing preemptible polling .
* uslepp_range ( ) is called between
* polling iterations .
2015-03-06 04:29:09 +03:00
* @ mc_io : MC I / O object to be used
2015-10-17 23:33:13 +03:00
* @ cmd : command buffer to receive MC response
* @ mc_status : MC command completion status
2015-03-06 04:29:09 +03:00
*/
2015-10-17 23:33:13 +03:00
static int mc_polling_wait_preemptible ( struct fsl_mc_io * mc_io ,
2018-03-15 20:05:31 +03:00
struct fsl_mc_command * cmd ,
2015-10-17 23:33:13 +03:00
enum mc_cmd_status * mc_status )
2015-03-06 04:29:09 +03:00
{
enum mc_cmd_status status ;
unsigned long jiffies_until_timeout =
2015-10-17 23:33:12 +03:00
jiffies + msecs_to_jiffies ( MC_CMD_COMPLETION_TIMEOUT_MS ) ;
2015-03-06 04:29:09 +03:00
/*
* Wait for response from the MC hardware :
*/
for ( ; ; ) {
status = mc_read_response ( mc_io - > portal_virt_addr , cmd ) ;
if ( status ! = MC_CMD_STATUS_READY )
break ;
/*
* TODO : When MC command completion interrupts are supported
* call wait function here instead of usleep_range ( )
*/
usleep_range ( MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS ,
MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS ) ;
if ( time_after_eq ( jiffies , jiffies_until_timeout ) ) {
2016-03-14 19:14:06 +03:00
dev_dbg ( mc_io - > dev ,
2017-07-19 14:42:29 +03:00
" MC command timed out (portal: %pa, dprc handle: %#x, command: %#x) \n " ,
& mc_io - > portal_phys_addr ,
2016-06-23 00:40:52 +03:00
( unsigned int ) mc_cmd_hdr_read_token ( cmd ) ,
( unsigned int ) mc_cmd_hdr_read_cmdid ( cmd ) ) ;
2015-03-06 04:29:09 +03:00
return - ETIMEDOUT ;
}
}
2015-10-17 23:33:13 +03:00
* mc_status = status ;
return 0 ;
}
2015-10-17 23:33:14 +03:00
/**
2021-06-17 14:04:54 +03:00
* mc_polling_wait_atomic ( ) - Waits for the completion of an MC command
* doing atomic polling . udelay ( ) is called
* between polling iterations .
2015-10-17 23:33:14 +03:00
* @ mc_io : MC I / O object to be used
* @ cmd : command buffer to receive MC response
* @ mc_status : MC command completion status
*/
static int mc_polling_wait_atomic ( struct fsl_mc_io * mc_io ,
2018-03-15 20:05:31 +03:00
struct fsl_mc_command * cmd ,
2015-10-17 23:33:14 +03:00
enum mc_cmd_status * mc_status )
{
enum mc_cmd_status status ;
unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000 ;
BUILD_BUG_ON ( ( MC_CMD_COMPLETION_TIMEOUT_MS * 1000 ) %
MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS ! = 0 ) ;
for ( ; ; ) {
status = mc_read_response ( mc_io - > portal_virt_addr , cmd ) ;
if ( status ! = MC_CMD_STATUS_READY )
break ;
udelay ( MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS ) ;
timeout_usecs - = MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS ;
if ( timeout_usecs = = 0 ) {
2016-03-14 19:14:06 +03:00
dev_dbg ( mc_io - > dev ,
2017-07-19 14:42:29 +03:00
" MC command timed out (portal: %pa, dprc handle: %#x, command: %#x) \n " ,
& mc_io - > portal_phys_addr ,
2016-06-23 00:40:52 +03:00
( unsigned int ) mc_cmd_hdr_read_token ( cmd ) ,
( unsigned int ) mc_cmd_hdr_read_cmdid ( cmd ) ) ;
2015-10-17 23:33:14 +03:00
return - ETIMEDOUT ;
}
}
* mc_status = status ;
return 0 ;
}
2015-10-17 23:33:13 +03:00
/**
2021-06-17 14:04:54 +03:00
* mc_send_command ( ) - Sends a command to the MC device using the given
* MC I / O object
2015-10-17 23:33:13 +03:00
* @ mc_io : MC I / O object to be used
* @ cmd : command to be sent
*
* Returns ' 0 ' on Success ; Error code otherwise .
*/
2018-03-15 20:05:31 +03:00
int mc_send_command ( struct fsl_mc_io * mc_io , struct fsl_mc_command * cmd )
2015-10-17 23:33:13 +03:00
{
int error ;
enum mc_cmd_status status ;
2015-10-17 23:33:15 +03:00
unsigned long irq_flags = 0 ;
2015-10-17 23:33:13 +03:00
2017-11-17 16:38:32 +03:00
if ( in_irq ( ) & & ! ( mc_io - > flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL ) )
2015-10-17 23:33:14 +03:00
return - EINVAL ;
2015-10-17 23:33:15 +03:00
if ( mc_io - > flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL )
2020-07-17 18:47:59 +03:00
raw_spin_lock_irqsave ( & mc_io - > spinlock , irq_flags ) ;
2015-10-17 23:33:15 +03:00
else
mutex_lock ( & mc_io - > mutex ) ;
2015-10-17 23:33:13 +03:00
/*
* Send command to the MC hardware :
*/
mc_write_command ( mc_io - > portal_virt_addr , cmd ) ;
/*
* Wait for response from the MC hardware :
*/
2015-10-17 23:33:14 +03:00
if ( ! ( mc_io - > flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL ) )
error = mc_polling_wait_preemptible ( mc_io , cmd , & status ) ;
else
error = mc_polling_wait_atomic ( mc_io , cmd , & status ) ;
2015-10-17 23:33:13 +03:00
if ( error < 0 )
2015-10-17 23:33:15 +03:00
goto common_exit ;
2015-10-17 23:33:13 +03:00
2015-03-06 04:29:09 +03:00
if ( status ! = MC_CMD_STATUS_OK ) {
2016-03-14 19:14:06 +03:00
dev_dbg ( mc_io - > dev ,
2017-07-19 14:42:29 +03:00
" MC command failed: portal: %pa, dprc handle: %#x, command: %#x, status: %s (%#x) \n " ,
& mc_io - > portal_phys_addr ,
2016-06-23 00:40:52 +03:00
( unsigned int ) mc_cmd_hdr_read_token ( cmd ) ,
( unsigned int ) mc_cmd_hdr_read_cmdid ( cmd ) ,
2015-03-06 04:29:09 +03:00
mc_status_to_string ( status ) ,
( unsigned int ) status ) ;
2015-10-17 23:33:15 +03:00
error = mc_status_to_error ( status ) ;
goto common_exit ;
2015-03-06 04:29:09 +03:00
}
2015-10-17 23:33:15 +03:00
error = 0 ;
common_exit :
if ( mc_io - > flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL )
2020-07-17 18:47:59 +03:00
raw_spin_unlock_irqrestore ( & mc_io - > spinlock , irq_flags ) ;
2015-10-17 23:33:15 +03:00
else
mutex_unlock ( & mc_io - > mutex ) ;
return error ;
2015-03-06 04:29:09 +03:00
}
2017-11-17 16:38:29 +03:00
EXPORT_SYMBOL_GPL ( mc_send_command ) ;