2021-06-18 12:37:12 -04:00
// SPDX-License-Identifier: GPL-2.0+
/*
* SPI driver for Renesas Synchronization Management Unit ( SMU ) devices .
*
* Copyright ( C ) 2021 Integrated Device Technology , Inc . , a Renesas Company .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/mfd/core.h>
# include <linux/mfd/rsmu.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include <linux/spi/spi.h>
# include "rsmu.h"
# define RSMU_CM_PAGE_ADDR 0x7C
# define RSMU_SABRE_PAGE_ADDR 0x7F
# define RSMU_HIGHER_ADDR_MASK 0xFF80
# define RSMU_HIGHER_ADDR_SHIFT 7
# define RSMU_LOWER_ADDR_MASK 0x7F
static int rsmu_read_device ( struct rsmu_ddata * rsmu , u8 reg , u8 * buf , u16 bytes )
{
struct spi_device * client = to_spi_device ( rsmu - > dev ) ;
struct spi_transfer xfer = { 0 } ;
struct spi_message msg ;
u8 cmd [ 256 ] = { 0 } ;
u8 rsp [ 256 ] = { 0 } ;
int ret ;
cmd [ 0 ] = reg | 0x80 ;
xfer . rx_buf = rsp ;
xfer . len = bytes + 1 ;
xfer . tx_buf = cmd ;
xfer . bits_per_word = client - > bits_per_word ;
xfer . speed_hz = client - > max_speed_hz ;
spi_message_init ( & msg ) ;
spi_message_add_tail ( & xfer , & msg ) ;
/*
* 4 - wire SPI is a shift register , so for every byte you send ,
* you get one back at the same time . Example read from 0xC024 ,
* which has value of 0x2D
*
* MOSI :
* 7 C 00 C0 # Set page register
* A4 00 # MSB is set , so this is read command
* MISO :
* XX 2 D # XX is a dummy byte from sending A4 and we
* need to throw it away
*/
ret = spi_sync ( client , & msg ) ;
if ( ret > = 0 )
memcpy ( buf , & rsp [ 1 ] , xfer . len - 1 ) ;
return ret ;
}
static int rsmu_write_device ( struct rsmu_ddata * rsmu , u8 reg , u8 * buf , u16 bytes )
{
struct spi_device * client = to_spi_device ( rsmu - > dev ) ;
struct spi_transfer xfer = { 0 } ;
struct spi_message msg ;
u8 cmd [ 256 ] = { 0 } ;
cmd [ 0 ] = reg ;
memcpy ( & cmd [ 1 ] , buf , bytes ) ;
xfer . len = bytes + 1 ;
xfer . tx_buf = cmd ;
xfer . bits_per_word = client - > bits_per_word ;
xfer . speed_hz = client - > max_speed_hz ;
spi_message_init ( & msg ) ;
spi_message_add_tail ( & xfer , & msg ) ;
return spi_sync ( client , & msg ) ;
}
/*
* 1 - byte ( 1 B ) offset addressing :
* 16 - bit register address : the lower 7 bits of the register address come
* from the offset addr byte and the upper 9 bits come from the page register .
*/
static int rsmu_write_page_register ( struct rsmu_ddata * rsmu , u16 reg )
{
u8 page_reg ;
u8 buf [ 2 ] ;
u16 bytes ;
u16 page ;
int err ;
switch ( rsmu - > type ) {
case RSMU_CM :
page_reg = RSMU_CM_PAGE_ADDR ;
page = reg & RSMU_HIGHER_ADDR_MASK ;
buf [ 0 ] = ( u8 ) ( page & 0xff ) ;
buf [ 1 ] = ( u8 ) ( ( page > > 8 ) & 0xff ) ;
bytes = 2 ;
break ;
case RSMU_SABRE :
page_reg = RSMU_SABRE_PAGE_ADDR ;
page = reg > > RSMU_HIGHER_ADDR_SHIFT ;
buf [ 0 ] = ( u8 ) ( page & 0xff ) ;
bytes = 1 ;
break ;
default :
dev_err ( rsmu - > dev , " Unsupported RSMU device type: %d \n " , rsmu - > type ) ;
return - ENODEV ;
}
/* Simply return if we are on the same page */
if ( rsmu - > page = = page )
return 0 ;
err = rsmu_write_device ( rsmu , page_reg , buf , bytes ) ;
if ( err )
dev_err ( rsmu - > dev , " Failed to set page offset 0x%x \n " , page ) ;
else
/* Remember the last page */
rsmu - > page = page ;
return err ;
}
static int rsmu_reg_read ( void * context , unsigned int reg , unsigned int * val )
{
struct rsmu_ddata * rsmu = spi_get_drvdata ( ( struct spi_device * ) context ) ;
u8 addr = ( u8 ) ( reg & RSMU_LOWER_ADDR_MASK ) ;
int err ;
err = rsmu_write_page_register ( rsmu , reg ) ;
if ( err )
return err ;
err = rsmu_read_device ( rsmu , addr , ( u8 * ) val , 1 ) ;
if ( err )
dev_err ( rsmu - > dev , " Failed to read offset address 0x%x \n " , addr ) ;
return err ;
}
static int rsmu_reg_write ( void * context , unsigned int reg , unsigned int val )
{
struct rsmu_ddata * rsmu = spi_get_drvdata ( ( struct spi_device * ) context ) ;
u8 addr = ( u8 ) ( reg & RSMU_LOWER_ADDR_MASK ) ;
u8 data = ( u8 ) val ;
int err ;
err = rsmu_write_page_register ( rsmu , reg ) ;
if ( err )
return err ;
err = rsmu_write_device ( rsmu , addr , & data , 1 ) ;
if ( err )
dev_err ( rsmu - > dev ,
" Failed to write offset address 0x%x \n " , addr ) ;
return err ;
}
static const struct regmap_config rsmu_cm_regmap_config = {
. reg_bits = 16 ,
. val_bits = 8 ,
. max_register = 0xD000 ,
. reg_read = rsmu_reg_read ,
. reg_write = rsmu_reg_write ,
. cache_type = REGCACHE_NONE ,
} ;
static const struct regmap_config rsmu_sabre_regmap_config = {
. reg_bits = 16 ,
. val_bits = 8 ,
. max_register = 0x400 ,
. reg_read = rsmu_reg_read ,
. reg_write = rsmu_reg_write ,
. cache_type = REGCACHE_NONE ,
} ;
static int rsmu_spi_probe ( struct spi_device * client )
{
const struct spi_device_id * id = spi_get_device_id ( client ) ;
const struct regmap_config * cfg ;
struct rsmu_ddata * rsmu ;
int ret ;
rsmu = devm_kzalloc ( & client - > dev , sizeof ( * rsmu ) , GFP_KERNEL ) ;
if ( ! rsmu )
return - ENOMEM ;
spi_set_drvdata ( client , rsmu ) ;
rsmu - > dev = & client - > dev ;
rsmu - > type = ( enum rsmu_type ) id - > driver_data ;
/* Initialize regmap */
switch ( rsmu - > type ) {
case RSMU_CM :
cfg = & rsmu_cm_regmap_config ;
break ;
case RSMU_SABRE :
cfg = & rsmu_sabre_regmap_config ;
break ;
default :
dev_err ( rsmu - > dev , " Unsupported RSMU device type: %d \n " , rsmu - > type ) ;
return - ENODEV ;
}
rsmu - > regmap = devm_regmap_init ( & client - > dev , NULL , client , cfg ) ;
if ( IS_ERR ( rsmu - > regmap ) ) {
ret = PTR_ERR ( rsmu - > regmap ) ;
dev_err ( rsmu - > dev , " Failed to allocate register map: %d \n " , ret ) ;
return ret ;
}
return rsmu_core_init ( rsmu ) ;
}
2022-01-23 18:52:01 +01:00
static void rsmu_spi_remove ( struct spi_device * client )
2021-06-18 12:37:12 -04:00
{
struct rsmu_ddata * rsmu = spi_get_drvdata ( client ) ;
rsmu_core_exit ( rsmu ) ;
}
static const struct spi_device_id rsmu_spi_id [ ] = {
{ " 8a34000 " , RSMU_CM } ,
{ " 8a34001 " , RSMU_CM } ,
{ " 82p33810 " , RSMU_SABRE } ,
{ " 82p33811 " , RSMU_SABRE } ,
{ }
} ;
MODULE_DEVICE_TABLE ( spi , rsmu_spi_id ) ;
static const struct of_device_id rsmu_spi_of_match [ ] = {
{ . compatible = " idt,8a34000 " , . data = ( void * ) RSMU_CM } ,
{ . compatible = " idt,8a34001 " , . data = ( void * ) RSMU_CM } ,
{ . compatible = " idt,82p33810 " , . data = ( void * ) RSMU_SABRE } ,
{ . compatible = " idt,82p33811 " , . data = ( void * ) RSMU_SABRE } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , rsmu_spi_of_match ) ;
static struct spi_driver rsmu_spi_driver = {
. driver = {
. name = " rsmu-spi " ,
. of_match_table = of_match_ptr ( rsmu_spi_of_match ) ,
} ,
. probe = rsmu_spi_probe ,
. remove = rsmu_spi_remove ,
. id_table = rsmu_spi_id ,
} ;
static int __init rsmu_spi_init ( void )
{
return spi_register_driver ( & rsmu_spi_driver ) ;
}
subsys_initcall ( rsmu_spi_init ) ;
static void __exit rsmu_spi_exit ( void )
{
spi_unregister_driver ( & rsmu_spi_driver ) ;
}
module_exit ( rsmu_spi_exit ) ;
MODULE_DESCRIPTION ( " Renesas SMU SPI driver " ) ;
MODULE_LICENSE ( " GPL " ) ;