2020-04-25 22:59:48 +03:00
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
//
// AMD SPI controller driver
//
// Copyright (c) 2020, Advanced Micro Devices, Inc.
//
// Author: Sanjay R Mehta <sanju.mehta@amd.com>
# include <linux/acpi.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/delay.h>
# include <linux/spi/spi.h>
2022-02-11 17:31:53 +03:00
# include <linux/iopoll.h>
2020-04-25 22:59:48 +03:00
# define AMD_SPI_CTRL0_REG 0x00
# define AMD_SPI_EXEC_CMD BIT(16)
# define AMD_SPI_FIFO_CLEAR BIT(20)
# define AMD_SPI_BUSY BIT(31)
2022-02-11 17:31:55 +03:00
# define AMD_SPI_OPCODE_REG 0x45
# define AMD_SPI_CMD_TRIGGER_REG 0x47
# define AMD_SPI_TRIGGER_CMD BIT(7)
2020-04-25 22:59:48 +03:00
# define AMD_SPI_OPCODE_MASK 0xFF
# define AMD_SPI_ALT_CS_REG 0x1D
# define AMD_SPI_ALT_CS_MASK 0x3
# define AMD_SPI_FIFO_BASE 0x80
# define AMD_SPI_TX_COUNT_REG 0x48
# define AMD_SPI_RX_COUNT_REG 0x4B
# define AMD_SPI_STATUS_REG 0x4C
2022-07-06 13:06:22 +03:00
# define AMD_SPI_FIFO_SIZE 70
2020-04-25 22:59:48 +03:00
# define AMD_SPI_MEM_SIZE 200
2022-08-25 17:31:32 +03:00
# define AMD_SPI_ENA_REG 0x20
# define AMD_SPI_ALT_SPD_SHIFT 20
# define AMD_SPI_ALT_SPD_MASK GENMASK(23, AMD_SPI_ALT_SPD_SHIFT)
# define AMD_SPI_SPI100_SHIFT 0
# define AMD_SPI_SPI100_MASK GENMASK(AMD_SPI_SPI100_SHIFT, AMD_SPI_SPI100_SHIFT)
# define AMD_SPI_SPEED_REG 0x6C
# define AMD_SPI_SPD7_SHIFT 8
# define AMD_SPI_SPD7_MASK GENMASK(13, AMD_SPI_SPD7_SHIFT)
# define AMD_SPI_MAX_HZ 100000000
# define AMD_SPI_MIN_HZ 800000
2022-07-06 13:06:26 +03:00
/**
* enum amd_spi_versions - SPI controller versions
* @ AMD_SPI_V1 : AMDI0061 hardware version
* @ AMD_SPI_V2 : AMDI0062 hardware version
*/
2022-02-11 17:31:55 +03:00
enum amd_spi_versions {
2022-07-06 13:06:26 +03:00
AMD_SPI_V1 = 1 ,
AMD_SPI_V2 ,
2022-02-11 17:31:55 +03:00
} ;
2022-08-25 17:31:32 +03:00
enum amd_spi_speed {
F_66_66MHz ,
F_33_33MHz ,
F_22_22MHz ,
F_16_66MHz ,
F_100MHz ,
F_800KHz ,
2022-11-04 13:06:37 +03:00
SPI_SPD7 = 0x7 ,
2022-08-25 17:31:32 +03:00
F_50MHz = 0x4 ,
F_4MHz = 0x32 ,
F_3_17MHz = 0x3F
} ;
/**
* struct amd_spi_freq - Matches device speed with values to write in regs
* @ speed_hz : Device frequency
* @ enable_val : Value to be written to " enable register "
* @ spd7_val : Some frequencies requires to have a value written at SPISPEED register
*/
struct amd_spi_freq {
u32 speed_hz ;
u32 enable_val ;
u32 spd7_val ;
} ;
2022-07-06 13:06:26 +03:00
/**
* struct amd_spi - SPI driver instance
* @ io_remap_addr : Start address of the SPI controller registers
* @ version : SPI controller hardware version
2022-08-25 17:31:32 +03:00
* @ speed_hz : Device frequency
2022-07-06 13:06:26 +03:00
*/
2020-04-25 22:59:48 +03:00
struct amd_spi {
void __iomem * io_remap_addr ;
2022-02-11 17:31:55 +03:00
enum amd_spi_versions version ;
2022-08-25 17:31:32 +03:00
unsigned int speed_hz ;
2020-04-25 22:59:48 +03:00
} ;
2021-09-10 14:15:26 +03:00
static inline u8 amd_spi_readreg8 ( struct amd_spi * amd_spi , int idx )
2020-04-25 22:59:48 +03:00
{
return ioread8 ( ( u8 __iomem * ) amd_spi - > io_remap_addr + idx ) ;
}
2021-09-10 14:15:26 +03:00
static inline void amd_spi_writereg8 ( struct amd_spi * amd_spi , int idx , u8 val )
2020-04-25 22:59:48 +03:00
{
iowrite8 ( val , ( ( u8 __iomem * ) amd_spi - > io_remap_addr + idx ) ) ;
}
2021-09-10 14:15:26 +03:00
static void amd_spi_setclear_reg8 ( struct amd_spi * amd_spi , int idx , u8 set , u8 clear )
2020-04-25 22:59:48 +03:00
{
2021-09-10 14:15:26 +03:00
u8 tmp = amd_spi_readreg8 ( amd_spi , idx ) ;
2020-04-25 22:59:48 +03:00
tmp = ( tmp & ~ clear ) | set ;
2021-09-10 14:15:26 +03:00
amd_spi_writereg8 ( amd_spi , idx , tmp ) ;
2020-04-25 22:59:48 +03:00
}
2021-09-10 14:15:26 +03:00
static inline u32 amd_spi_readreg32 ( struct amd_spi * amd_spi , int idx )
2020-04-25 22:59:48 +03:00
{
return ioread32 ( ( u8 __iomem * ) amd_spi - > io_remap_addr + idx ) ;
}
2021-09-10 14:15:26 +03:00
static inline void amd_spi_writereg32 ( struct amd_spi * amd_spi , int idx , u32 val )
2020-04-25 22:59:48 +03:00
{
iowrite32 ( val , ( ( u8 __iomem * ) amd_spi - > io_remap_addr + idx ) ) ;
}
2021-09-10 14:15:26 +03:00
static inline void amd_spi_setclear_reg32 ( struct amd_spi * amd_spi , int idx , u32 set , u32 clear )
2020-04-25 22:59:48 +03:00
{
2021-09-10 14:15:26 +03:00
u32 tmp = amd_spi_readreg32 ( amd_spi , idx ) ;
2020-04-25 22:59:48 +03:00
tmp = ( tmp & ~ clear ) | set ;
2021-09-10 14:15:26 +03:00
amd_spi_writereg32 ( amd_spi , idx , tmp ) ;
2020-04-25 22:59:48 +03:00
}
2021-09-10 14:15:28 +03:00
static void amd_spi_select_chip ( struct amd_spi * amd_spi , u8 cs )
2020-04-25 22:59:48 +03:00
{
2021-09-10 14:15:28 +03:00
amd_spi_setclear_reg8 ( amd_spi , AMD_SPI_ALT_CS_REG , cs , AMD_SPI_ALT_CS_MASK ) ;
2020-04-25 22:59:48 +03:00
}
2022-02-11 17:31:55 +03:00
static inline void amd_spi_clear_chip ( struct amd_spi * amd_spi , u8 chip_select )
{
amd_spi_writereg8 ( amd_spi , AMD_SPI_ALT_CS_REG , chip_select & ~ AMD_SPI_ALT_CS_MASK ) ;
}
2021-09-10 14:15:26 +03:00
static void amd_spi_clear_fifo_ptr ( struct amd_spi * amd_spi )
2020-04-25 22:59:48 +03:00
{
2021-09-10 14:15:26 +03:00
amd_spi_setclear_reg32 ( amd_spi , AMD_SPI_CTRL0_REG , AMD_SPI_FIFO_CLEAR , AMD_SPI_FIFO_CLEAR ) ;
2020-04-25 22:59:48 +03:00
}
2022-02-11 17:31:55 +03:00
static int amd_spi_set_opcode ( struct amd_spi * amd_spi , u8 cmd_opcode )
2020-04-25 22:59:48 +03:00
{
2022-02-11 17:31:55 +03:00
switch ( amd_spi - > version ) {
case AMD_SPI_V1 :
amd_spi_setclear_reg32 ( amd_spi , AMD_SPI_CTRL0_REG , cmd_opcode ,
AMD_SPI_OPCODE_MASK ) ;
return 0 ;
case AMD_SPI_V2 :
amd_spi_writereg8 ( amd_spi , AMD_SPI_OPCODE_REG , cmd_opcode ) ;
return 0 ;
default :
return - ENODEV ;
}
2020-04-25 22:59:48 +03:00
}
2021-09-10 14:15:26 +03:00
static inline void amd_spi_set_rx_count ( struct amd_spi * amd_spi , u8 rx_count )
2020-04-25 22:59:48 +03:00
{
2021-09-10 14:15:26 +03:00
amd_spi_setclear_reg8 ( amd_spi , AMD_SPI_RX_COUNT_REG , rx_count , 0xff ) ;
2020-04-25 22:59:48 +03:00
}
2021-09-10 14:15:26 +03:00
static inline void amd_spi_set_tx_count ( struct amd_spi * amd_spi , u8 tx_count )
2020-04-25 22:59:48 +03:00
{
2021-09-10 14:15:26 +03:00
amd_spi_setclear_reg8 ( amd_spi , AMD_SPI_TX_COUNT_REG , tx_count , 0xff ) ;
2020-04-25 22:59:48 +03:00
}
2021-09-10 14:15:27 +03:00
static int amd_spi_busy_wait ( struct amd_spi * amd_spi )
2020-04-25 22:59:48 +03:00
{
2022-02-11 17:31:53 +03:00
u32 val ;
2022-02-11 17:31:55 +03:00
int reg ;
switch ( amd_spi - > version ) {
case AMD_SPI_V1 :
reg = AMD_SPI_CTRL0_REG ;
break ;
case AMD_SPI_V2 :
reg = AMD_SPI_STATUS_REG ;
break ;
default :
return - ENODEV ;
}
2020-04-25 22:59:48 +03:00
2022-02-11 17:31:55 +03:00
return readl_poll_timeout ( amd_spi - > io_remap_addr + reg , val ,
! ( val & AMD_SPI_BUSY ) , 20 , 2000000 ) ;
2020-04-25 22:59:48 +03:00
}
2021-09-10 14:15:29 +03:00
static int amd_spi_execute_opcode ( struct amd_spi * amd_spi )
2020-04-25 22:59:48 +03:00
{
2021-09-10 14:15:29 +03:00
int ret ;
ret = amd_spi_busy_wait ( amd_spi ) ;
if ( ret )
return ret ;
2022-02-11 17:31:55 +03:00
switch ( amd_spi - > version ) {
case AMD_SPI_V1 :
/* Set ExecuteOpCode bit in the CTRL0 register */
amd_spi_setclear_reg32 ( amd_spi , AMD_SPI_CTRL0_REG , AMD_SPI_EXEC_CMD ,
AMD_SPI_EXEC_CMD ) ;
return 0 ;
case AMD_SPI_V2 :
/* Trigger the command execution */
amd_spi_setclear_reg8 ( amd_spi , AMD_SPI_CMD_TRIGGER_REG ,
AMD_SPI_TRIGGER_CMD , AMD_SPI_TRIGGER_CMD ) ;
return 0 ;
default :
return - ENODEV ;
}
2020-04-25 22:59:48 +03:00
}
2023-07-28 12:32:01 +03:00
static int amd_spi_host_setup ( struct spi_device * spi )
2020-04-25 22:59:48 +03:00
{
2023-07-28 12:32:01 +03:00
struct amd_spi * amd_spi = spi_controller_get_devdata ( spi - > controller ) ;
2020-04-25 22:59:48 +03:00
2021-09-10 14:15:26 +03:00
amd_spi_clear_fifo_ptr ( amd_spi ) ;
2020-04-25 22:59:48 +03:00
return 0 ;
}
2022-08-25 17:31:32 +03:00
static const struct amd_spi_freq amd_spi_freq [ ] = {
{ AMD_SPI_MAX_HZ , F_100MHz , 0 } ,
{ 66660000 , F_66_66MHz , 0 } ,
{ 50000000 , SPI_SPD7 , F_50MHz } ,
{ 33330000 , F_33_33MHz , 0 } ,
{ 22220000 , F_22_22MHz , 0 } ,
{ 16660000 , F_16_66MHz , 0 } ,
{ 4000000 , SPI_SPD7 , F_4MHz } ,
{ 3170000 , SPI_SPD7 , F_3_17MHz } ,
{ AMD_SPI_MIN_HZ , F_800KHz , 0 } ,
} ;
static int amd_set_spi_freq ( struct amd_spi * amd_spi , u32 speed_hz )
{
unsigned int i , spd7_val , alt_spd ;
if ( speed_hz < AMD_SPI_MIN_HZ )
return - EINVAL ;
for ( i = 0 ; i < ARRAY_SIZE ( amd_spi_freq ) ; i + + )
if ( speed_hz > = amd_spi_freq [ i ] . speed_hz )
break ;
2022-08-30 12:36:07 +03:00
if ( amd_spi - > speed_hz = = amd_spi_freq [ i ] . speed_hz )
2022-08-25 17:31:32 +03:00
return 0 ;
amd_spi - > speed_hz = amd_spi_freq [ i ] . speed_hz ;
alt_spd = ( amd_spi_freq [ i ] . enable_val < < AMD_SPI_ALT_SPD_SHIFT )
& AMD_SPI_ALT_SPD_MASK ;
amd_spi_setclear_reg32 ( amd_spi , AMD_SPI_ENA_REG , alt_spd ,
AMD_SPI_ALT_SPD_MASK ) ;
if ( amd_spi - > speed_hz = = AMD_SPI_MAX_HZ )
amd_spi_setclear_reg32 ( amd_spi , AMD_SPI_ENA_REG , 1 ,
AMD_SPI_SPI100_MASK ) ;
if ( amd_spi_freq [ i ] . spd7_val ) {
spd7_val = ( amd_spi_freq [ i ] . spd7_val < < AMD_SPI_SPD7_SHIFT )
& AMD_SPI_SPD7_MASK ;
amd_spi_setclear_reg32 ( amd_spi , AMD_SPI_SPEED_REG , spd7_val ,
AMD_SPI_SPD7_MASK ) ;
}
return 0 ;
}
2020-04-25 22:59:48 +03:00
static inline int amd_spi_fifo_xfer ( struct amd_spi * amd_spi ,
2023-07-28 12:32:01 +03:00
struct spi_controller * host ,
2020-04-25 22:59:48 +03:00
struct spi_message * message )
{
struct spi_transfer * xfer = NULL ;
2022-08-25 17:31:32 +03:00
struct spi_device * spi = message - > spi ;
2022-08-18 04:00:59 +03:00
u8 cmd_opcode = 0 , fifo_pos = AMD_SPI_FIFO_BASE ;
2020-04-25 22:59:48 +03:00
u8 * buf = NULL ;
u32 i = 0 ;
u32 tx_len = 0 , rx_len = 0 ;
list_for_each_entry ( xfer , & message - > transfers ,
transfer_list ) {
2022-08-25 17:31:32 +03:00
if ( xfer - > speed_hz )
amd_set_spi_freq ( amd_spi , xfer - > speed_hz ) ;
else
amd_set_spi_freq ( amd_spi , spi - > max_speed_hz ) ;
2022-08-18 04:00:59 +03:00
if ( xfer - > tx_buf ) {
2020-04-25 22:59:48 +03:00
buf = ( u8 * ) xfer - > tx_buf ;
2022-08-18 04:00:59 +03:00
if ( ! tx_len ) {
cmd_opcode = * ( u8 * ) xfer - > tx_buf ;
buf + + ;
xfer - > len - - ;
}
tx_len + = xfer - > len ;
2020-04-25 22:59:48 +03:00
/* Write data into the FIFO. */
2022-08-18 04:00:59 +03:00
for ( i = 0 ; i < xfer - > len ; i + + )
amd_spi_writereg8 ( amd_spi , fifo_pos + i , buf [ i ] ) ;
2020-04-25 22:59:48 +03:00
2022-08-18 04:00:59 +03:00
fifo_pos + = xfer - > len ;
2020-04-25 22:59:48 +03:00
}
2022-08-18 04:00:59 +03:00
/* Store no. of bytes to be received from FIFO */
if ( xfer - > rx_buf )
rx_len + = xfer - > len ;
}
if ( ! buf ) {
message - > status = - EINVAL ;
goto fin_msg ;
}
amd_spi_set_opcode ( amd_spi , cmd_opcode ) ;
amd_spi_set_tx_count ( amd_spi , tx_len ) ;
amd_spi_set_rx_count ( amd_spi , rx_len ) ;
/* Execute command */
message - > status = amd_spi_execute_opcode ( amd_spi ) ;
if ( message - > status )
goto fin_msg ;
if ( rx_len ) {
message - > status = amd_spi_busy_wait ( amd_spi ) ;
if ( message - > status )
goto fin_msg ;
list_for_each_entry ( xfer , & message - > transfers , transfer_list )
if ( xfer - > rx_buf ) {
buf = ( u8 * ) xfer - > rx_buf ;
/* Read data from FIFO to receive buffer */
for ( i = 0 ; i < xfer - > len ; i + + )
buf [ i ] = amd_spi_readreg8 ( amd_spi , fifo_pos + i ) ;
fifo_pos + = xfer - > len ;
}
2020-04-25 22:59:48 +03:00
}
/* Update statistics */
message - > actual_length = tx_len + rx_len + 1 ;
2022-02-11 17:31:55 +03:00
2022-08-18 04:00:59 +03:00
fin_msg :
2022-02-11 17:31:55 +03:00
switch ( amd_spi - > version ) {
case AMD_SPI_V1 :
break ;
case AMD_SPI_V2 :
2023-03-10 20:32:03 +03:00
amd_spi_clear_chip ( amd_spi , spi_get_chipselect ( message - > spi , 0 ) ) ;
2022-02-11 17:31:55 +03:00
break ;
default :
return - ENODEV ;
}
2023-07-28 12:32:01 +03:00
spi_finalize_current_message ( host ) ;
2020-04-25 22:59:48 +03:00
2022-08-18 04:00:59 +03:00
return message - > status ;
2020-04-25 22:59:48 +03:00
}
2023-07-28 12:32:01 +03:00
static int amd_spi_host_transfer ( struct spi_controller * host ,
2020-04-25 22:59:48 +03:00
struct spi_message * msg )
{
2023-07-28 12:32:01 +03:00
struct amd_spi * amd_spi = spi_controller_get_devdata ( host ) ;
2020-04-25 22:59:48 +03:00
struct spi_device * spi = msg - > spi ;
2023-03-10 20:32:03 +03:00
amd_spi_select_chip ( amd_spi , spi_get_chipselect ( spi , 0 ) ) ;
2020-04-25 22:59:48 +03:00
/*
* Extract spi_transfers from the spi message and
* program the controller .
*/
2023-07-28 12:32:01 +03:00
return amd_spi_fifo_xfer ( amd_spi , host , msg ) ;
2020-04-25 22:59:48 +03:00
}
2022-07-06 13:06:22 +03:00
static size_t amd_spi_max_transfer_size ( struct spi_device * spi )
{
return AMD_SPI_FIFO_SIZE ;
}
2020-04-25 22:59:48 +03:00
static int amd_spi_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
2023-07-28 12:32:01 +03:00
struct spi_controller * host ;
2020-04-25 22:59:48 +03:00
struct amd_spi * amd_spi ;
2022-07-06 13:06:23 +03:00
int err ;
2020-04-25 22:59:48 +03:00
2023-07-28 12:32:01 +03:00
/* Allocate storage for host and driver private data */
host = devm_spi_alloc_host ( dev , sizeof ( struct amd_spi ) ) ;
if ( ! host )
return dev_err_probe ( dev , - ENOMEM , " Error allocating SPI host \n " ) ;
2020-04-25 22:59:48 +03:00
2023-07-28 12:32:01 +03:00
amd_spi = spi_controller_get_devdata ( host ) ;
2020-11-21 06:43:51 +03:00
amd_spi - > io_remap_addr = devm_platform_ioremap_resource ( pdev , 0 ) ;
2022-07-06 13:06:24 +03:00
if ( IS_ERR ( amd_spi - > io_remap_addr ) )
return dev_err_probe ( dev , PTR_ERR ( amd_spi - > io_remap_addr ) ,
" ioremap of SPI registers failed \n " ) ;
2020-04-25 22:59:48 +03:00
dev_dbg ( dev , " io_remap_address: %p \n " , amd_spi - > io_remap_addr ) ;
2023-08-10 12:12:47 +03:00
amd_spi - > version = ( uintptr_t ) device_get_match_data ( dev ) ;
2022-02-11 17:31:55 +03:00
2023-07-28 12:32:01 +03:00
/* Initialize the spi_controller fields */
host - > bus_num = 0 ;
host - > num_chipselect = 4 ;
host - > mode_bits = 0 ;
host - > flags = SPI_CONTROLLER_HALF_DUPLEX ;
host - > max_speed_hz = AMD_SPI_MAX_HZ ;
host - > min_speed_hz = AMD_SPI_MIN_HZ ;
host - > setup = amd_spi_host_setup ;
host - > transfer_one_message = amd_spi_host_transfer ;
host - > max_transfer_size = amd_spi_max_transfer_size ;
host - > max_message_size = amd_spi_max_transfer_size ;
2020-04-25 22:59:48 +03:00
/* Register the controller with SPI framework */
2023-07-28 12:32:01 +03:00
err = devm_spi_register_controller ( dev , host ) ;
2022-07-06 13:06:23 +03:00
if ( err )
2022-07-06 13:06:24 +03:00
return dev_err_probe ( dev , err , " error registering SPI controller \n " ) ;
2020-04-25 22:59:48 +03:00
2022-07-06 13:06:24 +03:00
return 0 ;
2020-04-25 22:59:48 +03:00
}
2020-07-17 16:54:24 +03:00
# ifdef CONFIG_ACPI
2020-04-25 22:59:48 +03:00
static const struct acpi_device_id spi_acpi_match [ ] = {
2022-02-11 17:31:55 +03:00
{ " AMDI0061 " , AMD_SPI_V1 } ,
{ " AMDI0062 " , AMD_SPI_V2 } ,
2020-04-25 22:59:48 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , spi_acpi_match ) ;
2020-07-17 16:54:24 +03:00
# endif
2020-04-25 22:59:48 +03:00
static struct platform_driver amd_spi_driver = {
. driver = {
. name = " amd_spi " ,
2022-02-16 19:27:19 +03:00
. acpi_match_table = ACPI_PTR ( spi_acpi_match ) ,
2020-04-25 22:59:48 +03:00
} ,
. probe = amd_spi_probe ,
} ;
module_platform_driver ( amd_spi_driver ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_AUTHOR ( " Sanjay Mehta <sanju.mehta@amd.com> " ) ;
MODULE_DESCRIPTION ( " AMD SPI Master Controller Driver " ) ;