2020-08-17 10:10:21 +03:00
// SPDX-License-Identifier: GPL-2.0
2020-08-17 10:10:20 +03:00
# include <linux/delay.h>
# include <linux/err.h>
2020-08-17 10:10:29 +03:00
# include <linux/interrupt.h>
2020-08-17 10:10:20 +03:00
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
2020-08-17 10:10:29 +03:00
# include <linux/of.h>
# include <linux/platform_device.h>
2020-08-17 10:10:20 +03:00
# include <linux/seq_file.h>
2020-08-17 10:10:29 +03:00
# include <linux/slab.h>
2020-08-17 10:10:20 +03:00
# include <linux/spmi.h>
/*
* SPMI register addr
*/
2020-08-17 10:10:24 +03:00
# define SPMI_CHANNEL_OFFSET 0x0300
# define SPMI_SLAVE_OFFSET 0x20
2020-08-17 10:10:20 +03:00
2020-08-17 10:10:24 +03:00
# define SPMI_APB_SPMI_CMD_BASE_ADDR 0x0100
2020-08-17 10:10:21 +03:00
2020-08-17 10:10:20 +03:00
# define SPMI_APB_SPMI_WDATA0_BASE_ADDR 0x0104
# define SPMI_APB_SPMI_WDATA1_BASE_ADDR 0x0108
# define SPMI_APB_SPMI_WDATA2_BASE_ADDR 0x010c
# define SPMI_APB_SPMI_WDATA3_BASE_ADDR 0x0110
# define SPMI_APB_SPMI_STATUS_BASE_ADDR 0x0200
# define SPMI_APB_SPMI_RDATA0_BASE_ADDR 0x0204
# define SPMI_APB_SPMI_RDATA1_BASE_ADDR 0x0208
# define SPMI_APB_SPMI_RDATA2_BASE_ADDR 0x020c
# define SPMI_APB_SPMI_RDATA3_BASE_ADDR 0x0210
2020-08-17 10:10:24 +03:00
# define SPMI_PER_DATAREG_BYTE 4
2020-08-17 10:10:20 +03:00
/*
* SPMI cmd register
*/
2020-08-17 10:10:24 +03:00
# define SPMI_APB_SPMI_CMD_EN BIT(31)
2020-08-17 10:10:20 +03:00
# define SPMI_APB_SPMI_CMD_TYPE_OFFSET 24
# define SPMI_APB_SPMI_CMD_LENGTH_OFFSET 20
2020-08-17 10:10:24 +03:00
# define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET 16
# define SPMI_APB_SPMI_CMD_ADDR_OFFSET 0
2020-08-17 10:10:20 +03:00
/* Command Opcodes */
2020-08-17 10:10:21 +03:00
2020-08-17 10:10:20 +03:00
enum spmi_controller_cmd_op_code {
SPMI_CMD_REG_ZERO_WRITE = 0 ,
SPMI_CMD_REG_WRITE = 1 ,
SPMI_CMD_REG_READ = 2 ,
SPMI_CMD_EXT_REG_WRITE = 3 ,
SPMI_CMD_EXT_REG_READ = 4 ,
SPMI_CMD_EXT_REG_WRITE_L = 5 ,
SPMI_CMD_EXT_REG_READ_L = 6 ,
SPMI_CMD_REG_RESET = 7 ,
SPMI_CMD_REG_SLEEP = 8 ,
SPMI_CMD_REG_SHUTDOWN = 9 ,
SPMI_CMD_REG_WAKEUP = 10 ,
} ;
/*
* SPMI status register
*/
2020-08-17 10:10:24 +03:00
# define SPMI_APB_TRANS_DONE BIT(0)
# define SPMI_APB_TRANS_FAIL BIT(2)
2020-08-17 10:10:20 +03:00
/* Command register fields */
# define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT 16
/* Maximum number of support PMIC peripherals */
# define SPMI_CONTROLLER_TIMEOUT_US 1000
2020-08-17 10:10:24 +03:00
# define SPMI_CONTROLLER_MAX_TRANS_BYTES 16
2020-08-17 10:10:20 +03:00
struct spmi_controller_dev {
struct spmi_controller * controller ;
struct device * dev ;
void __iomem * base ;
spinlock_t lock ;
u32 channel ;
} ;
2020-08-17 10:10:27 +03:00
static int spmi_controller_wait_for_done ( struct device * dev ,
struct spmi_controller_dev * ctrl_dev ,
2020-08-17 10:10:21 +03:00
void __iomem * base , u8 sid , u16 addr )
2020-08-17 10:10:20 +03:00
{
u32 timeout = SPMI_CONTROLLER_TIMEOUT_US ;
2020-08-17 10:10:29 +03:00
u32 status , offset ;
2020-08-17 10:10:24 +03:00
offset = SPMI_APB_SPMI_STATUS_BASE_ADDR ;
offset + = SPMI_CHANNEL_OFFSET * ctrl_dev - > channel + SPMI_SLAVE_OFFSET * sid ;
2020-08-17 10:10:20 +03:00
2020-08-17 10:10:29 +03:00
do {
2020-08-17 10:10:21 +03:00
status = readl ( base + offset ) ;
2020-08-17 10:10:20 +03:00
if ( status & SPMI_APB_TRANS_DONE ) {
if ( status & SPMI_APB_TRANS_FAIL ) {
2020-08-17 10:10:27 +03:00
dev_err ( dev , " %s: transaction failed (0x%x) \n " ,
2020-08-17 10:10:20 +03:00
__func__ , status ) ;
return - EIO ;
}
2020-08-17 10:10:27 +03:00
dev_dbg ( dev , " %s: status 0x%x \n " , __func__ , status ) ;
2020-08-17 10:10:20 +03:00
return 0 ;
}
2020-08-17 10:10:21 +03:00
udelay ( 1 ) ;
2020-08-17 10:10:29 +03:00
} while ( timeout - - ) ;
2020-08-17 10:10:20 +03:00
2020-08-17 10:10:27 +03:00
dev_err ( dev , " %s: timeout, status 0x%x \n " , __func__ , status ) ;
2020-08-17 10:10:21 +03:00
return - ETIMEDOUT ;
}
2020-08-17 10:10:20 +03:00
static int spmi_read_cmd ( struct spmi_controller * ctrl ,
2020-08-17 10:10:29 +03:00
u8 opc , u8 slave_id , u16 slave_addr , u8 * __buf , size_t bc )
2020-08-17 10:10:20 +03:00
{
struct spmi_controller_dev * spmi_controller = dev_get_drvdata ( & ctrl - > dev ) ;
2020-08-17 10:10:29 +03:00
u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller - > channel ;
2020-08-17 10:10:20 +03:00
unsigned long flags ;
2020-08-17 10:10:26 +03:00
u8 * buf = __buf ;
2020-08-17 10:10:20 +03:00
u32 cmd , data ;
int rc ;
u8 op_code , i ;
if ( bc > SPMI_CONTROLLER_MAX_TRANS_BYTES ) {
2020-08-17 10:10:27 +03:00
dev_err ( & ctrl - > dev ,
2020-09-01 06:57:22 +03:00
" spmi_controller supports 1..%d bytes per trans, but:%zu requested \n " ,
2020-08-17 10:10:27 +03:00
SPMI_CONTROLLER_MAX_TRANS_BYTES , bc ) ;
2020-08-17 10:10:20 +03:00
return - EINVAL ;
}
2020-08-17 10:10:29 +03:00
switch ( opc ) {
case SPMI_CMD_READ :
2020-08-17 10:10:20 +03:00
op_code = SPMI_CMD_REG_READ ;
2020-08-17 10:10:29 +03:00
break ;
case SPMI_CMD_EXT_READ :
2020-08-17 10:10:20 +03:00
op_code = SPMI_CMD_EXT_REG_READ ;
2020-08-17 10:10:29 +03:00
break ;
case SPMI_CMD_EXT_READL :
2020-08-17 10:10:20 +03:00
op_code = SPMI_CMD_EXT_REG_READ_L ;
2020-08-17 10:10:29 +03:00
break ;
default :
dev_err ( & ctrl - > dev , " invalid read cmd 0x%x \n " , opc ) ;
2020-08-17 10:10:20 +03:00
return - EINVAL ;
}
2020-08-17 10:10:21 +03:00
cmd = SPMI_APB_SPMI_CMD_EN |
( op_code < < SPMI_APB_SPMI_CMD_TYPE_OFFSET ) |
( ( bc - 1 ) < < SPMI_APB_SPMI_CMD_LENGTH_OFFSET ) |
2020-08-17 10:10:29 +03:00
( ( slave_id & 0xf ) < < SPMI_APB_SPMI_CMD_SLAVEID_OFFSET ) | /* slvid */
( ( slave_addr & 0xffff ) < < SPMI_APB_SPMI_CMD_ADDR_OFFSET ) ; /* slave_addr */
2020-08-17 10:10:20 +03:00
2020-08-17 10:10:21 +03:00
spin_lock_irqsave ( & spmi_controller - > lock , flags ) ;
2020-08-17 10:10:20 +03:00
2020-08-17 10:10:21 +03:00
writel ( cmd , spmi_controller - > base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR ) ;
2020-08-17 10:10:20 +03:00
2020-08-17 10:10:27 +03:00
rc = spmi_controller_wait_for_done ( & ctrl - > dev , spmi_controller ,
2020-08-17 10:10:29 +03:00
spmi_controller - > base , slave_id , slave_addr ) ;
2020-08-17 10:10:20 +03:00
if ( rc )
goto done ;
2020-08-17 10:10:29 +03:00
for ( i = 0 ; bc > i * SPMI_PER_DATAREG_BYTE ; i + + ) {
data = readl ( spmi_controller - > base + chnl_ofst +
SPMI_SLAVE_OFFSET * slave_id +
SPMI_APB_SPMI_RDATA0_BASE_ADDR +
i * SPMI_PER_DATAREG_BYTE ) ;
2020-11-19 15:27:38 +03:00
data = be32_to_cpu ( ( __be32 __force ) data ) ;
2020-08-17 10:10:21 +03:00
if ( ( bc - i * SPMI_PER_DATAREG_BYTE ) > > 2 ) {
2020-08-17 10:10:20 +03:00
memcpy ( buf , & data , sizeof ( data ) ) ;
buf + = sizeof ( data ) ;
} else {
2020-08-17 10:10:21 +03:00
memcpy ( buf , & data , bc % SPMI_PER_DATAREG_BYTE ) ;
buf + = ( bc % SPMI_PER_DATAREG_BYTE ) ;
2020-08-17 10:10:20 +03:00
}
2020-08-17 10:10:29 +03:00
}
2020-08-17 10:10:20 +03:00
done :
spin_unlock_irqrestore ( & spmi_controller - > lock , flags ) ;
if ( rc )
2020-08-17 10:10:27 +03:00
dev_err ( & ctrl - > dev ,
2020-09-01 06:57:22 +03:00
" spmi read wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu \n " ,
2020-08-17 10:10:29 +03:00
opc , slave_id , slave_addr , bc + 1 ) ;
2020-08-17 10:10:26 +03:00
else
2020-08-17 10:10:29 +03:00
dev_dbg ( & ctrl - > dev , " %s: id:%d slave_addr:0x%x, read value: %*ph \n " ,
__func__ , slave_id , slave_addr , ( int ) bc , __buf ) ;
2020-08-17 10:10:26 +03:00
2020-08-17 10:10:20 +03:00
return rc ;
2020-08-17 10:10:21 +03:00
}
2020-08-17 10:10:20 +03:00
static int spmi_write_cmd ( struct spmi_controller * ctrl ,
2020-08-17 10:10:29 +03:00
u8 opc , u8 slave_id , u16 slave_addr , const u8 * __buf , size_t bc )
2020-08-17 10:10:20 +03:00
{
struct spmi_controller_dev * spmi_controller = dev_get_drvdata ( & ctrl - > dev ) ;
2020-08-17 10:10:29 +03:00
u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller - > channel ;
2020-08-17 10:10:26 +03:00
const u8 * buf = __buf ;
2020-08-17 10:10:20 +03:00
unsigned long flags ;
2020-08-17 10:10:25 +03:00
u32 cmd , data ;
2020-08-17 10:10:20 +03:00
int rc ;
u8 op_code , i ;
if ( bc > SPMI_CONTROLLER_MAX_TRANS_BYTES ) {
2020-08-17 10:10:27 +03:00
dev_err ( & ctrl - > dev ,
2020-09-01 06:57:22 +03:00
" spmi_controller supports 1..%d bytes per trans, but:%zu requested \n " ,
2020-08-17 10:10:27 +03:00
SPMI_CONTROLLER_MAX_TRANS_BYTES , bc ) ;
2020-08-17 10:10:20 +03:00
return - EINVAL ;
}
2020-08-17 10:10:29 +03:00
switch ( opc ) {
case SPMI_CMD_WRITE :
2020-08-17 10:10:20 +03:00
op_code = SPMI_CMD_REG_WRITE ;
2020-08-17 10:10:29 +03:00
break ;
case SPMI_CMD_EXT_WRITE :
2020-08-17 10:10:20 +03:00
op_code = SPMI_CMD_EXT_REG_WRITE ;
2020-08-17 10:10:29 +03:00
break ;
case SPMI_CMD_EXT_WRITEL :
2020-08-17 10:10:20 +03:00
op_code = SPMI_CMD_EXT_REG_WRITE_L ;
2020-08-17 10:10:29 +03:00
break ;
default :
dev_err ( & ctrl - > dev , " invalid write cmd 0x%x \n " , opc ) ;
2020-08-17 10:10:20 +03:00
return - EINVAL ;
}
2020-08-17 10:10:21 +03:00
cmd = SPMI_APB_SPMI_CMD_EN |
( op_code < < SPMI_APB_SPMI_CMD_TYPE_OFFSET ) |
( ( bc - 1 ) < < SPMI_APB_SPMI_CMD_LENGTH_OFFSET ) |
2020-08-17 10:10:29 +03:00
( ( slave_id & 0xf ) < < SPMI_APB_SPMI_CMD_SLAVEID_OFFSET ) |
( ( slave_addr & 0xffff ) < < SPMI_APB_SPMI_CMD_ADDR_OFFSET ) ;
2020-08-17 10:10:20 +03:00
/* Write data to FIFOs */
2020-08-17 10:10:21 +03:00
spin_lock_irqsave ( & spmi_controller - > lock , flags ) ;
2020-08-17 10:10:20 +03:00
2020-08-17 10:10:29 +03:00
for ( i = 0 ; bc > i * SPMI_PER_DATAREG_BYTE ; i + + ) {
2020-08-17 10:10:25 +03:00
data = 0 ;
2020-08-17 10:10:21 +03:00
if ( ( bc - i * SPMI_PER_DATAREG_BYTE ) > > 2 ) {
2020-08-17 10:10:20 +03:00
memcpy ( & data , buf , sizeof ( data ) ) ;
2020-08-17 10:10:21 +03:00
buf + = sizeof ( data ) ;
2020-08-17 10:10:20 +03:00
} else {
2020-08-17 10:10:21 +03:00
memcpy ( & data , buf , bc % SPMI_PER_DATAREG_BYTE ) ;
buf + = ( bc % SPMI_PER_DATAREG_BYTE ) ;
2020-08-17 10:10:20 +03:00
}
2020-11-19 15:27:38 +03:00
writel ( ( u32 __force ) cpu_to_be32 ( data ) ,
2020-08-17 10:10:29 +03:00
spmi_controller - > base + chnl_ofst +
SPMI_APB_SPMI_WDATA0_BASE_ADDR +
SPMI_PER_DATAREG_BYTE * i ) ;
}
2020-08-17 10:10:20 +03:00
/* Start the transaction */
2020-08-17 10:10:21 +03:00
writel ( cmd , spmi_controller - > base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR ) ;
2020-08-17 10:10:20 +03:00
2020-08-17 10:10:27 +03:00
rc = spmi_controller_wait_for_done ( & ctrl - > dev , spmi_controller ,
2020-08-17 10:10:29 +03:00
spmi_controller - > base , slave_id ,
slave_addr ) ;
2020-08-17 10:10:20 +03:00
spin_unlock_irqrestore ( & spmi_controller - > lock , flags ) ;
if ( rc )
2020-09-01 06:57:22 +03:00
dev_err ( & ctrl - > dev , " spmi write wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu \n " ,
2020-08-17 10:10:29 +03:00
opc , slave_id , slave_addr , bc ) ;
2020-08-17 10:10:26 +03:00
else
2020-08-17 10:10:29 +03:00
dev_dbg ( & ctrl - > dev , " %s: id:%d slave_addr:0x%x, wrote value: %*ph \n " ,
__func__ , slave_id , slave_addr , ( int ) bc , __buf ) ;
2020-08-17 10:10:20 +03:00
return rc ;
2020-08-17 10:10:21 +03:00
}
2020-08-17 10:10:20 +03:00
static int spmi_controller_probe ( struct platform_device * pdev )
{
struct spmi_controller_dev * spmi_controller ;
struct spmi_controller * ctrl ;
struct resource * iores ;
2020-08-17 10:10:29 +03:00
int ret ;
2020-08-17 10:10:26 +03:00
2020-08-17 10:10:20 +03:00
ctrl = spmi_controller_alloc ( & pdev - > dev , sizeof ( * spmi_controller ) ) ;
if ( ! ctrl ) {
dev_err ( & pdev - > dev , " can not allocate spmi_controller data \n " ) ;
2020-08-17 10:10:21 +03:00
return - ENOMEM ;
2020-08-17 10:10:20 +03:00
}
spmi_controller = spmi_controller_get_drvdata ( ctrl ) ;
spmi_controller - > controller = ctrl ;
iores = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! iores ) {
2020-08-17 10:10:21 +03:00
dev_err ( & pdev - > dev , " can not get resource! \n " ) ;
2020-12-13 18:11:05 +03:00
ret = - EINVAL ;
goto err_put_controller ;
2020-08-17 10:10:20 +03:00
}
2020-09-18 17:33:38 +03:00
spmi_controller - > base = devm_ioremap ( & pdev - > dev , iores - > start ,
resource_size ( iores ) ) ;
2020-08-17 10:10:20 +03:00
if ( ! spmi_controller - > base ) {
2020-08-17 10:10:21 +03:00
dev_err ( & pdev - > dev , " can not remap base addr! \n " ) ;
2020-12-13 18:11:05 +03:00
ret = - EADDRNOTAVAIL ;
goto err_put_controller ;
2020-08-17 10:10:20 +03:00
}
2021-06-24 17:01:31 +03:00
ret = of_property_read_u32 ( pdev - > dev . of_node , " hisilicon,spmi-channel " ,
2020-08-17 10:10:21 +03:00
& spmi_controller - > channel ) ;
2020-08-17 10:10:20 +03:00
if ( ret ) {
2020-08-17 10:10:23 +03:00
dev_err ( & pdev - > dev , " can not get channel \n " ) ;
2020-12-13 18:11:05 +03:00
ret = - ENODEV ;
goto err_put_controller ;
2020-08-17 10:10:20 +03:00
}
platform_set_drvdata ( pdev , spmi_controller ) ;
dev_set_drvdata ( & ctrl - > dev , spmi_controller ) ;
spin_lock_init ( & spmi_controller - > lock ) ;
ctrl - > nr = spmi_controller - > channel ;
ctrl - > dev . parent = pdev - > dev . parent ;
ctrl - > dev . of_node = of_node_get ( pdev - > dev . of_node ) ;
/* Callbacks */
ctrl - > read_cmd = spmi_read_cmd ;
ctrl - > write_cmd = spmi_write_cmd ;
ret = spmi_controller_add ( ctrl ) ;
2020-12-13 18:11:05 +03:00
if ( ret ) {
dev_err ( & pdev - > dev , " spmi_controller_add failed with error %d! \n " , ret ) ;
goto err_put_controller ;
}
return 0 ;
2020-08-17 10:10:22 +03:00
2020-12-13 18:11:05 +03:00
err_put_controller :
spmi_controller_put ( ctrl ) ;
2020-08-17 10:10:21 +03:00
return ret ;
2020-08-17 10:10:20 +03:00
}
static int spmi_del_controller ( struct platform_device * pdev )
{
struct spmi_controller * ctrl = platform_get_drvdata ( pdev ) ;
spmi_controller_remove ( ctrl ) ;
2020-12-13 18:11:05 +03:00
spmi_controller_put ( ctrl ) ;
2020-08-17 10:10:20 +03:00
return 0 ;
}
2020-08-17 10:10:21 +03:00
static const struct of_device_id spmi_controller_match_table [ ] = {
2020-08-18 17:58:55 +03:00
{
. compatible = " hisilicon,kirin970-spmi-controller " ,
2020-08-17 10:10:21 +03:00
} ,
{ }
2020-08-17 10:10:20 +03:00
} ;
2020-08-17 10:10:22 +03:00
MODULE_DEVICE_TABLE ( of , spmi_controller_match_table ) ;
2020-08-17 10:10:20 +03:00
static struct platform_driver spmi_controller_driver = {
. probe = spmi_controller_probe ,
. remove = spmi_del_controller ,
. driver = {
2020-08-17 10:10:29 +03:00
. name = " hisi_spmi_controller " ,
2020-08-17 10:10:20 +03:00
. of_match_table = spmi_controller_match_table ,
2020-08-17 10:10:21 +03:00
} ,
} ;
2020-08-17 10:10:20 +03:00
static int __init spmi_controller_init ( void )
{
2020-08-17 10:10:21 +03:00
return platform_driver_register ( & spmi_controller_driver ) ;
2020-08-17 10:10:20 +03:00
}
postcore_initcall ( spmi_controller_init ) ;
static void __exit spmi_controller_exit ( void )
{
platform_driver_unregister ( & spmi_controller_driver ) ;
}
module_exit ( spmi_controller_exit ) ;
2020-08-17 10:10:21 +03:00
2020-08-17 10:10:20 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;
2020-08-17 10:10:21 +03:00
MODULE_VERSION ( " 1.0 " ) ;
2020-08-20 10:51:36 +03:00
MODULE_ALIAS ( " platform:spmi_controller " ) ;