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>
# 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)
# 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
# define AMD_SPI_MEM_SIZE 200
/* M_CMD OP codes for SPI */
# define AMD_SPI_XFER_TX 1
# define AMD_SPI_XFER_RX 2
struct amd_spi {
void __iomem * io_remap_addr ;
unsigned long io_base_addr ;
u32 rom_addr ;
struct spi_master * master ;
u8 chip_select ;
} ;
static inline u8 amd_spi_readreg8 ( struct spi_master * master , int idx )
{
struct amd_spi * amd_spi = spi_master_get_devdata ( master ) ;
return ioread8 ( ( u8 __iomem * ) amd_spi - > io_remap_addr + idx ) ;
}
static inline void amd_spi_writereg8 ( struct spi_master * master , int idx ,
u8 val )
{
struct amd_spi * amd_spi = spi_master_get_devdata ( master ) ;
iowrite8 ( val , ( ( u8 __iomem * ) amd_spi - > io_remap_addr + idx ) ) ;
}
static inline void amd_spi_setclear_reg8 ( struct spi_master * master , int idx ,
u8 set , u8 clear )
{
u8 tmp = amd_spi_readreg8 ( master , idx ) ;
tmp = ( tmp & ~ clear ) | set ;
amd_spi_writereg8 ( master , idx , tmp ) ;
}
static inline u32 amd_spi_readreg32 ( struct spi_master * master , int idx )
{
struct amd_spi * amd_spi = spi_master_get_devdata ( master ) ;
return ioread32 ( ( u8 __iomem * ) amd_spi - > io_remap_addr + idx ) ;
}
static inline void amd_spi_writereg32 ( struct spi_master * master , int idx ,
u32 val )
{
struct amd_spi * amd_spi = spi_master_get_devdata ( master ) ;
iowrite32 ( val , ( ( u8 __iomem * ) amd_spi - > io_remap_addr + idx ) ) ;
}
static inline void amd_spi_setclear_reg32 ( struct spi_master * master , int idx ,
u32 set , u32 clear )
{
u32 tmp = amd_spi_readreg32 ( master , idx ) ;
tmp = ( tmp & ~ clear ) | set ;
amd_spi_writereg32 ( master , idx , tmp ) ;
}
static void amd_spi_select_chip ( struct spi_master * master )
{
struct amd_spi * amd_spi = spi_master_get_devdata ( master ) ;
u8 chip_select = amd_spi - > chip_select ;
amd_spi_setclear_reg8 ( master , AMD_SPI_ALT_CS_REG , chip_select ,
AMD_SPI_ALT_CS_MASK ) ;
}
static void amd_spi_clear_fifo_ptr ( struct spi_master * master )
{
amd_spi_setclear_reg32 ( master , AMD_SPI_CTRL0_REG , AMD_SPI_FIFO_CLEAR ,
AMD_SPI_FIFO_CLEAR ) ;
}
static void amd_spi_set_opcode ( struct spi_master * master , u8 cmd_opcode )
{
amd_spi_setclear_reg32 ( master , AMD_SPI_CTRL0_REG , cmd_opcode ,
AMD_SPI_OPCODE_MASK ) ;
}
static inline void amd_spi_set_rx_count ( struct spi_master * master ,
u8 rx_count )
{
amd_spi_setclear_reg8 ( master , AMD_SPI_RX_COUNT_REG , rx_count , 0xff ) ;
}
static inline void amd_spi_set_tx_count ( struct spi_master * master ,
u8 tx_count )
{
amd_spi_setclear_reg8 ( master , AMD_SPI_TX_COUNT_REG , tx_count , 0xff ) ;
}
static inline int amd_spi_busy_wait ( struct amd_spi * amd_spi )
{
bool spi_busy ;
int timeout = 100000 ;
/* poll for SPI bus to become idle */
spi_busy = ( ioread32 ( ( u8 __iomem * ) amd_spi - > io_remap_addr +
AMD_SPI_CTRL0_REG ) & AMD_SPI_BUSY ) = = AMD_SPI_BUSY ;
while ( spi_busy ) {
usleep_range ( 10 , 20 ) ;
if ( timeout - - < 0 )
return - ETIMEDOUT ;
spi_busy = ( ioread32 ( ( u8 __iomem * ) amd_spi - > io_remap_addr +
AMD_SPI_CTRL0_REG ) & AMD_SPI_BUSY ) = = AMD_SPI_BUSY ;
}
return 0 ;
}
static void amd_spi_execute_opcode ( struct spi_master * master )
{
struct amd_spi * amd_spi = spi_master_get_devdata ( master ) ;
/* Set ExecuteOpCode bit in the CTRL0 register */
amd_spi_setclear_reg32 ( master , AMD_SPI_CTRL0_REG , AMD_SPI_EXEC_CMD ,
AMD_SPI_EXEC_CMD ) ;
amd_spi_busy_wait ( amd_spi ) ;
}
static int amd_spi_master_setup ( struct spi_device * spi )
{
struct spi_master * master = spi - > master ;
amd_spi_clear_fifo_ptr ( master ) ;
return 0 ;
}
static inline int amd_spi_fifo_xfer ( struct amd_spi * amd_spi ,
struct spi_message * message )
{
struct spi_master * master = amd_spi - > master ;
struct spi_transfer * xfer = NULL ;
2020-04-28 07:56:41 +03:00
u8 cmd_opcode ;
2020-04-25 22:59:48 +03:00
u8 * buf = NULL ;
u32 m_cmd = 0 ;
u32 i = 0 ;
u32 tx_len = 0 , rx_len = 0 ;
list_for_each_entry ( xfer , & message - > transfers ,
transfer_list ) {
if ( xfer - > rx_buf )
m_cmd = AMD_SPI_XFER_RX ;
if ( xfer - > tx_buf )
m_cmd = AMD_SPI_XFER_TX ;
if ( m_cmd & AMD_SPI_XFER_TX ) {
buf = ( u8 * ) xfer - > tx_buf ;
tx_len = xfer - > len - 1 ;
cmd_opcode = * ( u8 * ) xfer - > tx_buf ;
buf + + ;
amd_spi_set_opcode ( master , cmd_opcode ) ;
/* Write data into the FIFO. */
for ( i = 0 ; i < tx_len ; i + + ) {
iowrite8 ( buf [ i ] ,
( ( u8 __iomem * ) amd_spi - > io_remap_addr +
AMD_SPI_FIFO_BASE + i ) ) ;
}
amd_spi_set_tx_count ( master , tx_len ) ;
amd_spi_clear_fifo_ptr ( master ) ;
/* Execute command */
amd_spi_execute_opcode ( master ) ;
}
if ( m_cmd & AMD_SPI_XFER_RX ) {
/*
* Store no . of bytes to be received from
* FIFO
*/
rx_len = xfer - > len ;
buf = ( u8 * ) xfer - > rx_buf ;
amd_spi_set_rx_count ( master , rx_len ) ;
amd_spi_clear_fifo_ptr ( master ) ;
/* Execute command */
amd_spi_execute_opcode ( master ) ;
/* Read data from FIFO to receive buffer */
for ( i = 0 ; i < rx_len ; i + + )
buf [ i ] = amd_spi_readreg8 ( master ,
AMD_SPI_FIFO_BASE +
tx_len + i ) ;
}
}
/* Update statistics */
message - > actual_length = tx_len + rx_len + 1 ;
/* complete the transaction */
message - > status = 0 ;
spi_finalize_current_message ( master ) ;
return 0 ;
}
static int amd_spi_master_transfer ( struct spi_master * master ,
struct spi_message * msg )
{
struct amd_spi * amd_spi = spi_master_get_devdata ( master ) ;
struct spi_device * spi = msg - > spi ;
amd_spi - > chip_select = spi - > chip_select ;
amd_spi_select_chip ( master ) ;
/*
* Extract spi_transfers from the spi message and
* program the controller .
*/
amd_spi_fifo_xfer ( amd_spi , msg ) ;
return 0 ;
}
static int amd_spi_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct spi_master * master ;
struct amd_spi * amd_spi ;
struct resource * res ;
int err = 0 ;
/* Allocate storage for spi_master and driver private data */
master = spi_alloc_master ( dev , sizeof ( struct amd_spi ) ) ;
if ( ! master ) {
dev_err ( dev , " Error allocating SPI master \n " ) ;
return - ENOMEM ;
}
amd_spi = spi_master_get_devdata ( master ) ;
amd_spi - > master = master ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
amd_spi - > io_remap_addr = devm_ioremap_resource ( & pdev - > dev , res ) ;
2020-04-29 05:54:26 +03:00
if ( IS_ERR ( amd_spi - > io_remap_addr ) ) {
err = PTR_ERR ( amd_spi - > io_remap_addr ) ;
2020-04-25 22:59:48 +03:00
dev_err ( dev , " error %d ioremap of SPI registers failed \n " , err ) ;
goto err_free_master ;
}
dev_dbg ( dev , " io_remap_address: %p \n " , amd_spi - > io_remap_addr ) ;
/* Initialize the spi_master fields */
master - > bus_num = 0 ;
master - > num_chipselect = 4 ;
master - > mode_bits = 0 ;
master - > flags = SPI_MASTER_HALF_DUPLEX ;
master - > setup = amd_spi_master_setup ;
master - > transfer_one_message = amd_spi_master_transfer ;
/* Register the controller with SPI framework */
err = spi_register_master ( master ) ;
if ( err ) {
dev_err ( dev , " error %d registering SPI controller \n " , err ) ;
2020-05-04 14:12:01 +03:00
goto err_free_master ;
2020-04-25 22:59:48 +03:00
}
platform_set_drvdata ( pdev , amd_spi ) ;
return 0 ;
err_free_master :
spi_master_put ( master ) ;
return 0 ;
}
static int amd_spi_remove ( struct platform_device * pdev )
{
struct amd_spi * amd_spi = platform_get_drvdata ( pdev ) ;
spi_unregister_master ( amd_spi - > master ) ;
spi_master_put ( amd_spi - > master ) ;
platform_set_drvdata ( pdev , NULL ) ;
return 0 ;
}
static const struct acpi_device_id spi_acpi_match [ ] = {
{ " AMDI0061 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , spi_acpi_match ) ;
static struct platform_driver amd_spi_driver = {
. driver = {
. name = " amd_spi " ,
. acpi_match_table = ACPI_PTR ( spi_acpi_match ) ,
} ,
. probe = amd_spi_probe ,
. remove = amd_spi_remove ,
} ;
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 " ) ;