2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-03-12 22:41:54 +04:00
/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
2013-03-12 22:41:46 +04:00
* Copyright ( c ) 2010 , Google Inc .
*
* Original authors : Code Aurora Forum
*
* Author : Dima Zavin < dima @ android . com >
* - Largely rewritten from original to not be an i2c driver .
*/
# define pr_fmt(fmt) "%s: " fmt, __func__
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
2013-03-12 22:41:54 +04:00
# include <linux/ssbi.h>
2013-03-12 22:41:46 +04:00
# include <linux/module.h>
2013-03-12 22:41:50 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2013-03-12 22:41:46 +04:00
/* SSBI 2.0 controller registers */
# define SSBI2_CMD 0x0008
# define SSBI2_RD 0x0010
# define SSBI2_STATUS 0x0014
# define SSBI2_MODE2 0x001C
/* SSBI_CMD fields */
# define SSBI_CMD_RDWRN (1 << 24)
/* SSBI_STATUS fields */
# define SSBI_STATUS_RD_READY (1 << 2)
# define SSBI_STATUS_READY (1 << 1)
# define SSBI_STATUS_MCHN_BUSY (1 << 0)
/* SSBI_MODE2 fields */
# define SSBI_MODE2_REG_ADDR_15_8_SHFT 0x04
# define SSBI_MODE2_REG_ADDR_15_8_MASK (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT)
# define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \
( ( ( MD ) & 0x0F ) | ( ( ( ( AD ) > > 8 ) < < SSBI_MODE2_REG_ADDR_15_8_SHFT ) & \
SSBI_MODE2_REG_ADDR_15_8_MASK ) )
/* SSBI PMIC Arbiter command registers */
# define SSBI_PA_CMD 0x0000
# define SSBI_PA_RD_STATUS 0x0004
/* SSBI_PA_CMD fields */
# define SSBI_PA_CMD_RDWRN (1 << 24)
# define SSBI_PA_CMD_ADDR_MASK 0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/
/* SSBI_PA_RD_STATUS fields */
# define SSBI_PA_RD_STATUS_TRANS_DONE (1 << 27)
# define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26)
# define SSBI_TIMEOUT_US 100
2013-12-11 03:35:16 +04:00
enum ssbi_controller_type {
MSM_SBI_CTRL_SSBI = 0 ,
MSM_SBI_CTRL_SSBI2 ,
MSM_SBI_CTRL_PMIC_ARBITER ,
} ;
2013-03-12 22:41:54 +04:00
struct ssbi {
2013-03-12 22:41:46 +04:00
struct device * slave ;
void __iomem * base ;
spinlock_t lock ;
2013-03-12 22:41:54 +04:00
enum ssbi_controller_type controller_type ;
int ( * read ) ( struct ssbi * , u16 addr , u8 * buf , int len ) ;
2013-12-11 03:35:17 +04:00
int ( * write ) ( struct ssbi * , u16 addr , const u8 * buf , int len ) ;
2013-03-12 22:41:46 +04:00
} ;
2013-03-12 22:41:54 +04:00
static inline u32 ssbi_readl ( struct ssbi * ssbi , u32 reg )
2013-03-12 22:41:46 +04:00
{
return readl ( ssbi - > base + reg ) ;
}
2013-03-12 22:41:54 +04:00
static inline void ssbi_writel ( struct ssbi * ssbi , u32 val , u32 reg )
2013-03-12 22:41:46 +04:00
{
writel ( val , ssbi - > base + reg ) ;
}
2013-03-12 22:41:51 +04:00
/*
* Via private exchange with one of the original authors , the hardware
* should generally finish a transaction in about 5u s . The worst
* case , is when using the arbiter and both other CPUs have just
* started trying to use the SSBI bus will result in a time of about
* 20u s . It should never take longer than this .
*
* As such , this wait merely spins , with a udelay .
*/
2013-03-12 22:41:54 +04:00
static int ssbi_wait_mask ( struct ssbi * ssbi , u32 set_mask , u32 clr_mask )
2013-03-12 22:41:46 +04:00
{
u32 timeout = SSBI_TIMEOUT_US ;
u32 val ;
while ( timeout - - ) {
val = ssbi_readl ( ssbi , SSBI2_STATUS ) ;
if ( ( ( val & set_mask ) = = set_mask ) & & ( ( val & clr_mask ) = = 0 ) )
return 0 ;
udelay ( 1 ) ;
}
return - ETIMEDOUT ;
}
static int
2013-03-12 22:41:54 +04:00
ssbi_read_bytes ( struct ssbi * ssbi , u16 addr , u8 * buf , int len )
2013-03-12 22:41:46 +04:00
{
u32 cmd = SSBI_CMD_RDWRN | ( ( addr & 0xff ) < < 16 ) ;
int ret = 0 ;
if ( ssbi - > controller_type = = MSM_SBI_CTRL_SSBI2 ) {
u32 mode2 = ssbi_readl ( ssbi , SSBI2_MODE2 ) ;
mode2 = SET_SSBI_MODE2_REG_ADDR_15_8 ( mode2 , addr ) ;
ssbi_writel ( ssbi , mode2 , SSBI2_MODE2 ) ;
}
while ( len ) {
ret = ssbi_wait_mask ( ssbi , SSBI_STATUS_READY , 0 ) ;
if ( ret )
goto err ;
ssbi_writel ( ssbi , cmd , SSBI2_CMD ) ;
ret = ssbi_wait_mask ( ssbi , SSBI_STATUS_RD_READY , 0 ) ;
if ( ret )
goto err ;
* buf + + = ssbi_readl ( ssbi , SSBI2_RD ) & 0xff ;
len - - ;
}
err :
return ret ;
}
static int
2013-12-11 03:35:17 +04:00
ssbi_write_bytes ( struct ssbi * ssbi , u16 addr , const u8 * buf , int len )
2013-03-12 22:41:46 +04:00
{
int ret = 0 ;
if ( ssbi - > controller_type = = MSM_SBI_CTRL_SSBI2 ) {
u32 mode2 = ssbi_readl ( ssbi , SSBI2_MODE2 ) ;
mode2 = SET_SSBI_MODE2_REG_ADDR_15_8 ( mode2 , addr ) ;
ssbi_writel ( ssbi , mode2 , SSBI2_MODE2 ) ;
}
while ( len ) {
ret = ssbi_wait_mask ( ssbi , SSBI_STATUS_READY , 0 ) ;
if ( ret )
goto err ;
ssbi_writel ( ssbi , ( ( addr & 0xff ) < < 16 ) | * buf , SSBI2_CMD ) ;
ret = ssbi_wait_mask ( ssbi , 0 , SSBI_STATUS_MCHN_BUSY ) ;
if ( ret )
goto err ;
buf + + ;
len - - ;
}
err :
return ret ;
}
2013-03-12 22:41:51 +04:00
/*
* See ssbi_wait_mask for an explanation of the time and the
* busywait .
*/
2013-03-12 22:41:46 +04:00
static inline int
2013-03-12 22:41:54 +04:00
ssbi_pa_transfer ( struct ssbi * ssbi , u32 cmd , u8 * data )
2013-03-12 22:41:46 +04:00
{
u32 timeout = SSBI_TIMEOUT_US ;
u32 rd_status = 0 ;
ssbi_writel ( ssbi , cmd , SSBI_PA_CMD ) ;
while ( timeout - - ) {
rd_status = ssbi_readl ( ssbi , SSBI_PA_RD_STATUS ) ;
2013-03-12 22:41:53 +04:00
if ( rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED )
2013-03-12 22:41:46 +04:00
return - EPERM ;
if ( rd_status & SSBI_PA_RD_STATUS_TRANS_DONE ) {
if ( data )
* data = rd_status & 0xff ;
return 0 ;
}
udelay ( 1 ) ;
}
return - ETIMEDOUT ;
}
static int
2013-03-12 22:41:54 +04:00
ssbi_pa_read_bytes ( struct ssbi * ssbi , u16 addr , u8 * buf , int len )
2013-03-12 22:41:46 +04:00
{
u32 cmd ;
int ret = 0 ;
cmd = SSBI_PA_CMD_RDWRN | ( addr & SSBI_PA_CMD_ADDR_MASK ) < < 8 ;
while ( len ) {
2013-03-12 22:41:54 +04:00
ret = ssbi_pa_transfer ( ssbi , cmd , buf ) ;
2013-03-12 22:41:46 +04:00
if ( ret )
goto err ;
buf + + ;
len - - ;
}
err :
return ret ;
}
static int
2013-12-11 03:35:17 +04:00
ssbi_pa_write_bytes ( struct ssbi * ssbi , u16 addr , const u8 * buf , int len )
2013-03-12 22:41:46 +04:00
{
u32 cmd ;
int ret = 0 ;
while ( len ) {
cmd = ( addr & SSBI_PA_CMD_ADDR_MASK ) < < 8 | * buf ;
2013-03-12 22:41:54 +04:00
ret = ssbi_pa_transfer ( ssbi , cmd , NULL ) ;
2013-03-12 22:41:46 +04:00
if ( ret )
goto err ;
buf + + ;
len - - ;
}
err :
return ret ;
}
2013-03-12 22:41:54 +04:00
int ssbi_read ( struct device * dev , u16 addr , u8 * buf , int len )
2013-03-12 22:41:46 +04:00
{
2019-05-09 17:23:39 +03:00
struct ssbi * ssbi = dev_get_drvdata ( dev ) ;
2013-03-12 22:41:46 +04:00
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & ssbi - > lock , flags ) ;
ret = ssbi - > read ( ssbi , addr , buf , len ) ;
spin_unlock_irqrestore ( & ssbi - > lock , flags ) ;
return ret ;
}
2013-03-12 22:41:54 +04:00
EXPORT_SYMBOL_GPL ( ssbi_read ) ;
2013-03-12 22:41:46 +04:00
2013-12-11 03:35:17 +04:00
int ssbi_write ( struct device * dev , u16 addr , const u8 * buf , int len )
2013-03-12 22:41:46 +04:00
{
2019-05-09 17:23:39 +03:00
struct ssbi * ssbi = dev_get_drvdata ( dev ) ;
2013-03-12 22:41:46 +04:00
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & ssbi - > lock , flags ) ;
ret = ssbi - > write ( ssbi , addr , buf , len ) ;
spin_unlock_irqrestore ( & ssbi - > lock , flags ) ;
return ret ;
}
2013-03-12 22:41:54 +04:00
EXPORT_SYMBOL_GPL ( ssbi_write ) ;
2013-03-12 22:41:46 +04:00
2013-03-12 22:41:54 +04:00
static int ssbi_probe ( struct platform_device * pdev )
2013-03-12 22:41:46 +04:00
{
2013-03-12 22:41:50 +04:00
struct device_node * np = pdev - > dev . of_node ;
2013-03-12 22:41:54 +04:00
struct ssbi * ssbi ;
2013-03-12 22:41:50 +04:00
const char * type ;
2013-03-12 22:41:46 +04:00
2013-06-03 23:39:44 +04:00
ssbi = devm_kzalloc ( & pdev - > dev , sizeof ( * ssbi ) , GFP_KERNEL ) ;
if ( ! ssbi )
2013-03-12 22:41:46 +04:00
return - ENOMEM ;
2023-02-08 12:35:44 +03:00
ssbi - > base = devm_platform_get_and_ioremap_resource ( pdev , 0 , NULL ) ;
2013-06-03 23:39:44 +04:00
if ( IS_ERR ( ssbi - > base ) )
return PTR_ERR ( ssbi - > base ) ;
2013-03-12 22:41:46 +04:00
platform_set_drvdata ( pdev , ssbi ) ;
2013-03-12 22:41:50 +04:00
type = of_get_property ( np , " qcom,controller-type " , NULL ) ;
if ( type = = NULL ) {
2013-06-03 23:39:44 +04:00
dev_err ( & pdev - > dev , " Missing qcom,controller-type property \n " ) ;
return - EINVAL ;
2013-03-12 22:41:50 +04:00
}
dev_info ( & pdev - > dev , " SSBI controller type: '%s' \n " , type ) ;
if ( strcmp ( type , " ssbi " ) = = 0 )
ssbi - > controller_type = MSM_SBI_CTRL_SSBI ;
else if ( strcmp ( type , " ssbi2 " ) = = 0 )
ssbi - > controller_type = MSM_SBI_CTRL_SSBI2 ;
else if ( strcmp ( type , " pmic-arbiter " ) = = 0 )
ssbi - > controller_type = MSM_SBI_CTRL_PMIC_ARBITER ;
else {
2013-06-03 23:39:44 +04:00
dev_err ( & pdev - > dev , " Unknown qcom,controller-type \n " ) ;
return - EINVAL ;
2013-03-12 22:41:50 +04:00
}
2013-03-12 22:41:46 +04:00
if ( ssbi - > controller_type = = MSM_SBI_CTRL_PMIC_ARBITER ) {
2013-03-12 22:41:54 +04:00
ssbi - > read = ssbi_pa_read_bytes ;
ssbi - > write = ssbi_pa_write_bytes ;
2013-03-12 22:41:46 +04:00
} else {
2013-03-12 22:41:54 +04:00
ssbi - > read = ssbi_read_bytes ;
ssbi - > write = ssbi_write_bytes ;
2013-03-12 22:41:46 +04:00
}
spin_lock_init ( & ssbi - > lock ) ;
2017-10-25 17:15:01 +03:00
return devm_of_platform_populate ( & pdev - > dev ) ;
2013-03-12 22:41:46 +04:00
}
2013-12-11 03:35:19 +04:00
static const struct of_device_id ssbi_match_table [ ] = {
2013-03-12 22:41:50 +04:00
{ . compatible = " qcom,ssbi " } ,
{ }
} ;
2013-06-03 23:39:43 +04:00
MODULE_DEVICE_TABLE ( of , ssbi_match_table ) ;
2013-03-12 22:41:50 +04:00
2013-03-12 22:41:54 +04:00
static struct platform_driver ssbi_driver = {
. probe = ssbi_probe ,
2013-03-12 22:41:46 +04:00
. driver = {
2013-03-12 22:41:54 +04:00
. name = " ssbi " ,
2013-03-12 22:41:50 +04:00
. of_match_table = ssbi_match_table ,
2013-03-12 22:41:46 +04:00
} ,
} ;
2013-06-03 23:39:44 +04:00
module_platform_driver ( ssbi_driver ) ;
2013-03-12 22:41:46 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_VERSION ( " 1.0 " ) ;
2013-03-12 22:41:54 +04:00
MODULE_ALIAS ( " platform:ssbi " ) ;
2013-03-12 22:41:46 +04:00
MODULE_AUTHOR ( " Dima Zavin <dima@android.com> " ) ;