2015-10-23 21:41:31 +03:00
/*
* RSB ( Reduced Serial Bus ) driver .
*
* Author : Chen - Yu Tsai < wens @ csie . org >
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed " as is " without any warranty of any
* kind , whether express or implied .
*
* The RSB controller looks like an SMBus controller which only supports
* byte and word data transfers . But , it differs from standard SMBus
* protocol on several aspects :
* - it uses addresses set at runtime to address slaves . Runtime addresses
* are sent to slaves using their 12 bit hardware addresses . Up to 15
* runtime addresses are available .
* - it adds a parity bit every 8 bits of data and address for read and
* write accesses ; this replaces the ack bit
* - only one read access is required to read a byte ( instead of a write
* followed by a read access in standard SMBus protocol )
* - there ' s no Ack bit after each read access
*
* This means this bus cannot be used to interface with standard SMBus
* devices . Devices known to support this interface include the AXP223 ,
* AXP809 , and AXP806 PMICs , and the AC100 audio codec , all from X - Powers .
*
* A description of the operation and wire protocol can be found in the
* RSB section of Allwinner ' s A80 user manual , which can be found at
*
* https : //github.com/allwinner-zh/documents/tree/master/A80
*
* This document is officially released by Allwinner .
*
* This driver is based on i2c - sun6i - p2wi . c , the P2WI bus driver .
*
*/
# include <linux/clk.h>
# include <linux/clk/clk-conf.h>
# include <linux/device.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/reset.h>
# include <linux/slab.h>
# include <linux/sunxi-rsb.h>
# include <linux/types.h>
/* RSB registers */
# define RSB_CTRL 0x0 /* Global control */
# define RSB_CCR 0x4 /* Clock control */
# define RSB_INTE 0x8 /* Interrupt controls */
# define RSB_INTS 0xc /* Interrupt status */
# define RSB_ADDR 0x10 /* Address to send with read/write command */
# define RSB_DATA 0x1c /* Data to read/write */
# define RSB_LCR 0x24 /* Line control */
# define RSB_DMCR 0x28 /* Device mode (init) control */
# define RSB_CMD 0x2c /* RSB Command */
# define RSB_DAR 0x30 /* Device address / runtime address */
/* CTRL fields */
# define RSB_CTRL_START_TRANS BIT(7)
# define RSB_CTRL_ABORT_TRANS BIT(6)
# define RSB_CTRL_GLOBAL_INT_ENB BIT(1)
# define RSB_CTRL_SOFT_RST BIT(0)
/* CLK CTRL fields */
# define RSB_CCR_SDA_OUT_DELAY(v) (((v) & 0x7) << 8)
# define RSB_CCR_MAX_CLK_DIV 0xff
# define RSB_CCR_CLK_DIV(v) ((v) & RSB_CCR_MAX_CLK_DIV)
/* STATUS fields */
# define RSB_INTS_TRANS_ERR_ACK BIT(16)
# define RSB_INTS_TRANS_ERR_DATA_BIT(v) (((v) >> 8) & 0xf)
# define RSB_INTS_TRANS_ERR_DATA GENMASK(11, 8)
# define RSB_INTS_LOAD_BSY BIT(2)
# define RSB_INTS_TRANS_ERR BIT(1)
# define RSB_INTS_TRANS_OVER BIT(0)
/* LINE CTRL fields*/
# define RSB_LCR_SCL_STATE BIT(5)
# define RSB_LCR_SDA_STATE BIT(4)
# define RSB_LCR_SCL_CTL BIT(3)
# define RSB_LCR_SCL_CTL_EN BIT(2)
# define RSB_LCR_SDA_CTL BIT(1)
# define RSB_LCR_SDA_CTL_EN BIT(0)
/* DEVICE MODE CTRL field values */
# define RSB_DMCR_DEVICE_START BIT(31)
# define RSB_DMCR_MODE_DATA (0x7c << 16)
# define RSB_DMCR_MODE_REG (0x3e << 8)
# define RSB_DMCR_DEV_ADDR 0x00
/* CMD values */
# define RSB_CMD_RD8 0x8b
# define RSB_CMD_RD16 0x9c
# define RSB_CMD_RD32 0xa6
# define RSB_CMD_WR8 0x4e
# define RSB_CMD_WR16 0x59
# define RSB_CMD_WR32 0x63
# define RSB_CMD_STRA 0xe8
/* DAR fields */
# define RSB_DAR_RTA(v) (((v) & 0xff) << 16)
# define RSB_DAR_DA(v) ((v) & 0xffff)
# define RSB_MAX_FREQ 20000000
# define RSB_CTRL_NAME "sunxi-rsb"
struct sunxi_rsb_addr_map {
u16 hwaddr ;
u8 rtaddr ;
} ;
struct sunxi_rsb {
struct device * dev ;
void __iomem * regs ;
struct clk * clk ;
struct reset_control * rstc ;
struct completion complete ;
struct mutex lock ;
unsigned int status ;
} ;
/* bus / slave device related functions */
static struct bus_type sunxi_rsb_bus ;
static int sunxi_rsb_device_match ( struct device * dev , struct device_driver * drv )
{
return of_driver_match_device ( dev , drv ) ;
}
static int sunxi_rsb_device_probe ( struct device * dev )
{
const struct sunxi_rsb_driver * drv = to_sunxi_rsb_driver ( dev - > driver ) ;
struct sunxi_rsb_device * rdev = to_sunxi_rsb_device ( dev ) ;
int ret ;
if ( ! drv - > probe )
return - ENODEV ;
if ( ! rdev - > irq ) {
int irq = - ENOENT ;
if ( dev - > of_node )
irq = of_irq_get ( dev - > of_node , 0 ) ;
if ( irq = = - EPROBE_DEFER )
return irq ;
if ( irq < 0 )
irq = 0 ;
rdev - > irq = irq ;
}
ret = of_clk_set_defaults ( dev - > of_node , false ) ;
if ( ret < 0 )
return ret ;
return drv - > probe ( rdev ) ;
}
static int sunxi_rsb_device_remove ( struct device * dev )
{
const struct sunxi_rsb_driver * drv = to_sunxi_rsb_driver ( dev - > driver ) ;
return drv - > remove ( to_sunxi_rsb_device ( dev ) ) ;
}
static struct bus_type sunxi_rsb_bus = {
. name = RSB_CTRL_NAME ,
. match = sunxi_rsb_device_match ,
. probe = sunxi_rsb_device_probe ,
. remove = sunxi_rsb_device_remove ,
} ;
static void sunxi_rsb_dev_release ( struct device * dev )
{
struct sunxi_rsb_device * rdev = to_sunxi_rsb_device ( dev ) ;
kfree ( rdev ) ;
}
/**
* sunxi_rsb_device_create ( ) - allocate and add an RSB device
* @ rsb : RSB controller
* @ node : RSB slave device node
* @ hwaddr : RSB slave hardware address
* @ rtaddr : RSB slave runtime address
*/
static struct sunxi_rsb_device * sunxi_rsb_device_create ( struct sunxi_rsb * rsb ,
struct device_node * node , u16 hwaddr , u8 rtaddr )
{
int err ;
struct sunxi_rsb_device * rdev ;
rdev = kzalloc ( sizeof ( * rdev ) , GFP_KERNEL ) ;
if ( ! rdev )
return ERR_PTR ( - ENOMEM ) ;
rdev - > rsb = rsb ;
rdev - > hwaddr = hwaddr ;
rdev - > rtaddr = rtaddr ;
rdev - > dev . bus = & sunxi_rsb_bus ;
rdev - > dev . parent = rsb - > dev ;
rdev - > dev . of_node = node ;
rdev - > dev . release = sunxi_rsb_dev_release ;
dev_set_name ( & rdev - > dev , " %s-%x " , RSB_CTRL_NAME , hwaddr ) ;
err = device_register ( & rdev - > dev ) ;
if ( err < 0 ) {
dev_err ( & rdev - > dev , " Can't add %s, status %d \n " ,
dev_name ( & rdev - > dev ) , err ) ;
goto err_device_add ;
}
dev_dbg ( & rdev - > dev , " device %s registered \n " , dev_name ( & rdev - > dev ) ) ;
err_device_add :
put_device ( & rdev - > dev ) ;
return ERR_PTR ( err ) ;
}
/**
* sunxi_rsb_device_unregister ( ) : unregister an RSB device
* @ rdev : rsb_device to be removed
*/
static void sunxi_rsb_device_unregister ( struct sunxi_rsb_device * rdev )
{
device_unregister ( & rdev - > dev ) ;
}
static int sunxi_rsb_remove_devices ( struct device * dev , void * data )
{
struct sunxi_rsb_device * rdev = to_sunxi_rsb_device ( dev ) ;
if ( dev - > bus = = & sunxi_rsb_bus )
sunxi_rsb_device_unregister ( rdev ) ;
return 0 ;
}
/**
* sunxi_rsb_driver_register ( ) - Register device driver with RSB core
* @ rdrv : device driver to be associated with slave - device .
*
* This API will register the client driver with the RSB framework .
* It is typically called from the driver ' s module - init function .
*/
int sunxi_rsb_driver_register ( struct sunxi_rsb_driver * rdrv )
{
rdrv - > driver . bus = & sunxi_rsb_bus ;
return driver_register ( & rdrv - > driver ) ;
}
EXPORT_SYMBOL_GPL ( sunxi_rsb_driver_register ) ;
/* common code that starts a transfer */
static int _sunxi_rsb_run_xfer ( struct sunxi_rsb * rsb )
{
if ( readl ( rsb - > regs + RSB_CTRL ) & RSB_CTRL_START_TRANS ) {
dev_dbg ( rsb - > dev , " RSB transfer still in progress \n " ) ;
return - EBUSY ;
}
reinit_completion ( & rsb - > complete ) ;
writel ( RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR | RSB_INTS_TRANS_OVER ,
rsb - > regs + RSB_INTE ) ;
writel ( RSB_CTRL_START_TRANS | RSB_CTRL_GLOBAL_INT_ENB ,
rsb - > regs + RSB_CTRL ) ;
if ( ! wait_for_completion_io_timeout ( & rsb - > complete ,
msecs_to_jiffies ( 100 ) ) ) {
dev_dbg ( rsb - > dev , " RSB timeout \n " ) ;
/* abort the transfer */
writel ( RSB_CTRL_ABORT_TRANS , rsb - > regs + RSB_CTRL ) ;
/* clear any interrupt flags */
writel ( readl ( rsb - > regs + RSB_INTS ) , rsb - > regs + RSB_INTS ) ;
return - ETIMEDOUT ;
}
if ( rsb - > status & RSB_INTS_LOAD_BSY ) {
dev_dbg ( rsb - > dev , " RSB busy \n " ) ;
return - EBUSY ;
}
if ( rsb - > status & RSB_INTS_TRANS_ERR ) {
if ( rsb - > status & RSB_INTS_TRANS_ERR_ACK ) {
dev_dbg ( rsb - > dev , " RSB slave nack \n " ) ;
return - EINVAL ;
}
if ( rsb - > status & RSB_INTS_TRANS_ERR_DATA ) {
dev_dbg ( rsb - > dev , " RSB transfer data error \n " ) ;
return - EIO ;
}
}
return 0 ;
}
static int sunxi_rsb_read ( struct sunxi_rsb * rsb , u8 rtaddr , u8 addr ,
u32 * buf , size_t len )
{
u32 cmd ;
int ret ;
if ( ! buf )
return - EINVAL ;
switch ( len ) {
case 1 :
cmd = RSB_CMD_RD8 ;
break ;
case 2 :
cmd = RSB_CMD_RD16 ;
break ;
case 4 :
cmd = RSB_CMD_RD32 ;
break ;
default :
2015-12-22 15:27:43 +03:00
dev_err ( rsb - > dev , " Invalid access width: %zd \n " , len ) ;
2015-10-23 21:41:31 +03:00
return - EINVAL ;
}
mutex_lock ( & rsb - > lock ) ;
writel ( addr , rsb - > regs + RSB_ADDR ) ;
writel ( RSB_DAR_RTA ( rtaddr ) , rsb - > regs + RSB_DAR ) ;
writel ( cmd , rsb - > regs + RSB_CMD ) ;
ret = _sunxi_rsb_run_xfer ( rsb ) ;
if ( ret )
2015-11-04 01:02:44 +03:00
goto unlock ;
2015-10-23 21:41:31 +03:00
* buf = readl ( rsb - > regs + RSB_DATA ) ;
2015-11-04 01:02:44 +03:00
unlock :
2015-10-23 21:41:31 +03:00
mutex_unlock ( & rsb - > lock ) ;
return ret ;
}
static int sunxi_rsb_write ( struct sunxi_rsb * rsb , u8 rtaddr , u8 addr ,
const u32 * buf , size_t len )
{
u32 cmd ;
int ret ;
if ( ! buf )
return - EINVAL ;
switch ( len ) {
case 1 :
cmd = RSB_CMD_WR8 ;
break ;
case 2 :
cmd = RSB_CMD_WR16 ;
break ;
case 4 :
cmd = RSB_CMD_WR32 ;
break ;
default :
2015-12-22 15:27:43 +03:00
dev_err ( rsb - > dev , " Invalid access width: %zd \n " , len ) ;
2015-10-23 21:41:31 +03:00
return - EINVAL ;
}
mutex_lock ( & rsb - > lock ) ;
writel ( addr , rsb - > regs + RSB_ADDR ) ;
writel ( RSB_DAR_RTA ( rtaddr ) , rsb - > regs + RSB_DAR ) ;
writel ( * buf , rsb - > regs + RSB_DATA ) ;
writel ( cmd , rsb - > regs + RSB_CMD ) ;
ret = _sunxi_rsb_run_xfer ( rsb ) ;
mutex_unlock ( & rsb - > lock ) ;
return ret ;
}
/* RSB regmap functions */
struct sunxi_rsb_ctx {
struct sunxi_rsb_device * rdev ;
int size ;
} ;
static int regmap_sunxi_rsb_reg_read ( void * context , unsigned int reg ,
unsigned int * val )
{
struct sunxi_rsb_ctx * ctx = context ;
struct sunxi_rsb_device * rdev = ctx - > rdev ;
if ( reg > 0xff )
return - EINVAL ;
return sunxi_rsb_read ( rdev - > rsb , rdev - > rtaddr , reg , val , ctx - > size ) ;
}
static int regmap_sunxi_rsb_reg_write ( void * context , unsigned int reg ,
unsigned int val )
{
struct sunxi_rsb_ctx * ctx = context ;
struct sunxi_rsb_device * rdev = ctx - > rdev ;
return sunxi_rsb_write ( rdev - > rsb , rdev - > rtaddr , reg , & val , ctx - > size ) ;
}
static void regmap_sunxi_rsb_free_ctx ( void * context )
{
struct sunxi_rsb_ctx * ctx = context ;
kfree ( ctx ) ;
}
static struct regmap_bus regmap_sunxi_rsb = {
. reg_write = regmap_sunxi_rsb_reg_write ,
. reg_read = regmap_sunxi_rsb_reg_read ,
. free_context = regmap_sunxi_rsb_free_ctx ,
. reg_format_endian_default = REGMAP_ENDIAN_NATIVE ,
. val_format_endian_default = REGMAP_ENDIAN_NATIVE ,
} ;
static struct sunxi_rsb_ctx * regmap_sunxi_rsb_init_ctx ( struct sunxi_rsb_device * rdev ,
const struct regmap_config * config )
{
struct sunxi_rsb_ctx * ctx ;
switch ( config - > val_bits ) {
case 8 :
case 16 :
case 32 :
break ;
default :
return ERR_PTR ( - EINVAL ) ;
}
ctx = kzalloc ( sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! ctx )
return ERR_PTR ( - ENOMEM ) ;
ctx - > rdev = rdev ;
ctx - > size = config - > val_bits / 8 ;
return ctx ;
}
struct regmap * __devm_regmap_init_sunxi_rsb ( struct sunxi_rsb_device * rdev ,
const struct regmap_config * config ,
struct lock_class_key * lock_key ,
const char * lock_name )
{
struct sunxi_rsb_ctx * ctx = regmap_sunxi_rsb_init_ctx ( rdev , config ) ;
if ( IS_ERR ( ctx ) )
return ERR_CAST ( ctx ) ;
return __devm_regmap_init ( & rdev - > dev , & regmap_sunxi_rsb , ctx , config ,
lock_key , lock_name ) ;
}
EXPORT_SYMBOL_GPL ( __devm_regmap_init_sunxi_rsb ) ;
/* RSB controller driver functions */
static irqreturn_t sunxi_rsb_irq ( int irq , void * dev_id )
{
struct sunxi_rsb * rsb = dev_id ;
u32 status ;
status = readl ( rsb - > regs + RSB_INTS ) ;
rsb - > status = status ;
/* Clear interrupts */
status & = ( RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR |
RSB_INTS_TRANS_OVER ) ;
writel ( status , rsb - > regs + RSB_INTS ) ;
complete ( & rsb - > complete ) ;
return IRQ_HANDLED ;
}
static int sunxi_rsb_init_device_mode ( struct sunxi_rsb * rsb )
{
int ret = 0 ;
u32 reg ;
/* send init sequence */
writel ( RSB_DMCR_DEVICE_START | RSB_DMCR_MODE_DATA |
RSB_DMCR_MODE_REG | RSB_DMCR_DEV_ADDR , rsb - > regs + RSB_DMCR ) ;
readl_poll_timeout ( rsb - > regs + RSB_DMCR , reg ,
! ( reg & RSB_DMCR_DEVICE_START ) , 100 , 250000 ) ;
if ( reg & RSB_DMCR_DEVICE_START )
ret = - ETIMEDOUT ;
/* clear interrupt status bits */
writel ( readl ( rsb - > regs + RSB_INTS ) , rsb - > regs + RSB_INTS ) ;
return ret ;
}
/*
* There are 15 valid runtime addresses , though Allwinner typically
* skips the first , for unknown reasons , and uses the following three .
*
* 0x17 , 0x2d , 0x3a , 0x4e , 0x59 , 0x63 , 0x74 , 0x8b ,
* 0x9c , 0xa6 , 0xb1 , 0xc5 , 0xd2 , 0xe8 , 0xff
*
* No designs with 2 RSB slave devices sharing identical hardware
* addresses on the same bus have been seen in the wild . All designs
* use 0x2d for the primary PMIC , 0x3a for the secondary PMIC if
* there is one , and 0x45 for peripheral ICs .
*
* The hardware does not seem to support re - setting runtime addresses .
* Attempts to do so result in the slave devices returning a NACK .
* Hence we just hardcode the mapping here , like Allwinner does .
*/
static const struct sunxi_rsb_addr_map sunxi_rsb_addr_maps [ ] = {
2015-12-16 12:14:45 +03:00
{ 0x3a3 , 0x2d } , /* Primary PMIC: AXP223, AXP809, AXP81X, ... */
2015-10-23 21:41:31 +03:00
{ 0x745 , 0x3a } , /* Secondary PMIC: AXP806, ... */
2015-12-16 12:14:46 +03:00
{ 0xe89 , 0x4e } , /* Peripheral IC: AC100, ... */
2015-10-23 21:41:31 +03:00
} ;
static u8 sunxi_rsb_get_rtaddr ( u16 hwaddr )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( sunxi_rsb_addr_maps ) ; i + + )
if ( hwaddr = = sunxi_rsb_addr_maps [ i ] . hwaddr )
return sunxi_rsb_addr_maps [ i ] . rtaddr ;
return 0 ; /* 0 is an invalid runtime address */
}
static int of_rsb_register_devices ( struct sunxi_rsb * rsb )
{
struct device * dev = rsb - > dev ;
struct device_node * child , * np = dev - > of_node ;
u32 hwaddr ;
u8 rtaddr ;
int ret ;
if ( ! np )
return - EINVAL ;
/* Runtime addresses for all slaves should be set first */
for_each_available_child_of_node ( np , child ) {
2017-07-19 00:42:51 +03:00
dev_dbg ( dev , " setting child %pOF runtime address \n " ,
child ) ;
2015-10-23 21:41:31 +03:00
ret = of_property_read_u32 ( child , " reg " , & hwaddr ) ;
if ( ret ) {
2017-07-19 00:42:51 +03:00
dev_err ( dev , " %pOF: invalid 'reg' property: %d \n " ,
child , ret ) ;
2015-10-23 21:41:31 +03:00
continue ;
}
rtaddr = sunxi_rsb_get_rtaddr ( hwaddr ) ;
if ( ! rtaddr ) {
2017-07-19 00:42:51 +03:00
dev_err ( dev , " %pOF: unknown hardware device address \n " ,
child ) ;
2015-10-23 21:41:31 +03:00
continue ;
}
/*
* Since no devices have been registered yet , we are the
* only ones using the bus , we can skip locking the bus .
*/
/* setup command parameters */
writel ( RSB_CMD_STRA , rsb - > regs + RSB_CMD ) ;
writel ( RSB_DAR_RTA ( rtaddr ) | RSB_DAR_DA ( hwaddr ) ,
rsb - > regs + RSB_DAR ) ;
/* send command */
ret = _sunxi_rsb_run_xfer ( rsb ) ;
if ( ret )
2017-07-19 00:42:51 +03:00
dev_warn ( dev , " %pOF: set runtime address failed: %d \n " ,
child , ret ) ;
2015-10-23 21:41:31 +03:00
}
/* Then we start adding devices and probing them */
for_each_available_child_of_node ( np , child ) {
struct sunxi_rsb_device * rdev ;
2017-07-19 00:42:51 +03:00
dev_dbg ( dev , " adding child %pOF \n " , child ) ;
2015-10-23 21:41:31 +03:00
ret = of_property_read_u32 ( child , " reg " , & hwaddr ) ;
if ( ret )
continue ;
rtaddr = sunxi_rsb_get_rtaddr ( hwaddr ) ;
if ( ! rtaddr )
continue ;
rdev = sunxi_rsb_device_create ( rsb , child , hwaddr , rtaddr ) ;
if ( IS_ERR ( rdev ) )
2017-07-19 00:42:51 +03:00
dev_err ( dev , " failed to add child device %pOF: %ld \n " ,
child , PTR_ERR ( rdev ) ) ;
2015-10-23 21:41:31 +03:00
}
return 0 ;
}
static const struct of_device_id sunxi_rsb_of_match_table [ ] = {
{ . compatible = " allwinner,sun8i-a23-rsb " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , sunxi_rsb_of_match_table ) ;
static int sunxi_rsb_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct resource * r ;
struct sunxi_rsb * rsb ;
unsigned long p_clk_freq ;
u32 clk_delay , clk_freq = 3000000 ;
int clk_div , irq , ret ;
u32 reg ;
of_property_read_u32 ( np , " clock-frequency " , & clk_freq ) ;
if ( clk_freq > RSB_MAX_FREQ ) {
dev_err ( dev ,
" clock-frequency (%u Hz) is too high (max = 20MHz) \n " ,
clk_freq ) ;
return - EINVAL ;
}
rsb = devm_kzalloc ( dev , sizeof ( * rsb ) , GFP_KERNEL ) ;
if ( ! rsb )
return - ENOMEM ;
rsb - > dev = dev ;
platform_set_drvdata ( pdev , rsb ) ;
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
rsb - > regs = devm_ioremap_resource ( dev , r ) ;
if ( IS_ERR ( rsb - > regs ) )
return PTR_ERR ( rsb - > regs ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
dev_err ( dev , " failed to retrieve irq: %d \n " , irq ) ;
return irq ;
}
rsb - > clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( rsb - > clk ) ) {
ret = PTR_ERR ( rsb - > clk ) ;
dev_err ( dev , " failed to retrieve clk: %d \n " , ret ) ;
return ret ;
}
ret = clk_prepare_enable ( rsb - > clk ) ;
if ( ret ) {
dev_err ( dev , " failed to enable clk: %d \n " , ret ) ;
return ret ;
}
p_clk_freq = clk_get_rate ( rsb - > clk ) ;
rsb - > rstc = devm_reset_control_get ( dev , NULL ) ;
if ( IS_ERR ( rsb - > rstc ) ) {
ret = PTR_ERR ( rsb - > rstc ) ;
dev_err ( dev , " failed to retrieve reset controller: %d \n " , ret ) ;
goto err_clk_disable ;
}
ret = reset_control_deassert ( rsb - > rstc ) ;
if ( ret ) {
dev_err ( dev , " failed to deassert reset line: %d \n " , ret ) ;
goto err_clk_disable ;
}
init_completion ( & rsb - > complete ) ;
mutex_init ( & rsb - > lock ) ;
/* reset the controller */
writel ( RSB_CTRL_SOFT_RST , rsb - > regs + RSB_CTRL ) ;
readl_poll_timeout ( rsb - > regs + RSB_CTRL , reg ,
! ( reg & RSB_CTRL_SOFT_RST ) , 1000 , 100000 ) ;
/*
* Clock frequency and delay calculation code is from
* Allwinner U - boot sources .
*
* From A83 user manual :
* bus clock frequency = parent clock frequency / ( 2 * ( divider + 1 ) )
*/
clk_div = p_clk_freq / clk_freq / 2 ;
if ( ! clk_div )
clk_div = 1 ;
else if ( clk_div > RSB_CCR_MAX_CLK_DIV + 1 )
clk_div = RSB_CCR_MAX_CLK_DIV + 1 ;
clk_delay = clk_div > > 1 ;
if ( ! clk_delay )
clk_delay = 1 ;
dev_info ( dev , " RSB running at %lu Hz \n " , p_clk_freq / clk_div / 2 ) ;
writel ( RSB_CCR_SDA_OUT_DELAY ( clk_delay ) | RSB_CCR_CLK_DIV ( clk_div - 1 ) ,
rsb - > regs + RSB_CCR ) ;
ret = devm_request_irq ( dev , irq , sunxi_rsb_irq , 0 , RSB_CTRL_NAME , rsb ) ;
if ( ret ) {
dev_err ( dev , " can't register interrupt handler irq %d: %d \n " ,
irq , ret ) ;
goto err_reset_assert ;
}
/* initialize all devices on the bus into RSB mode */
ret = sunxi_rsb_init_device_mode ( rsb ) ;
if ( ret )
dev_warn ( dev , " Initialize device mode failed: %d \n " , ret ) ;
of_rsb_register_devices ( rsb ) ;
return 0 ;
err_reset_assert :
reset_control_assert ( rsb - > rstc ) ;
err_clk_disable :
clk_disable_unprepare ( rsb - > clk ) ;
return ret ;
}
static int sunxi_rsb_remove ( struct platform_device * pdev )
{
struct sunxi_rsb * rsb = platform_get_drvdata ( pdev ) ;
device_for_each_child ( rsb - > dev , NULL , sunxi_rsb_remove_devices ) ;
reset_control_assert ( rsb - > rstc ) ;
clk_disable_unprepare ( rsb - > clk ) ;
return 0 ;
}
static struct platform_driver sunxi_rsb_driver = {
. probe = sunxi_rsb_probe ,
. remove = sunxi_rsb_remove ,
. driver = {
. name = RSB_CTRL_NAME ,
. of_match_table = sunxi_rsb_of_match_table ,
} ,
} ;
static int __init sunxi_rsb_init ( void )
{
int ret ;
ret = bus_register ( & sunxi_rsb_bus ) ;
if ( ret ) {
pr_err ( " failed to register sunxi sunxi_rsb bus: %d \n " , ret ) ;
return ret ;
}
return platform_driver_register ( & sunxi_rsb_driver ) ;
}
module_init ( sunxi_rsb_init ) ;
static void __exit sunxi_rsb_exit ( void )
{
platform_driver_unregister ( & sunxi_rsb_driver ) ;
bus_unregister ( & sunxi_rsb_bus ) ;
}
module_exit ( sunxi_rsb_exit ) ;
MODULE_AUTHOR ( " Chen-Yu Tsai <wens@csie.org> " ) ;
MODULE_DESCRIPTION ( " Allwinner sunXi Reduced Serial Bus controller driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;