2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-10-23 19:51:59 +09:00
/*
* Copyright ( C ) 2015 Masahiro Yamada < yamada . masahiro @ socionext . com >
*/
# include <linux/clk.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# define UNIPHIER_I2C_DTRM 0x00 /* TX register */
# define UNIPHIER_I2C_DTRM_IRQEN BIT(11) /* enable interrupt */
# define UNIPHIER_I2C_DTRM_STA BIT(10) /* start condition */
# define UNIPHIER_I2C_DTRM_STO BIT(9) /* stop condition */
# define UNIPHIER_I2C_DTRM_NACK BIT(8) /* do not return ACK */
# define UNIPHIER_I2C_DTRM_RD BIT(0) /* read transaction */
# define UNIPHIER_I2C_DREC 0x04 /* RX register */
# define UNIPHIER_I2C_DREC_MST BIT(14) /* 1 = master, 0 = slave */
# define UNIPHIER_I2C_DREC_TX BIT(13) /* 1 = transmit, 0 = receive */
# define UNIPHIER_I2C_DREC_STS BIT(12) /* stop condition detected */
# define UNIPHIER_I2C_DREC_LRB BIT(11) /* no ACK */
# define UNIPHIER_I2C_DREC_LAB BIT(9) /* arbitration lost */
# define UNIPHIER_I2C_DREC_BBN BIT(8) /* bus not busy */
# define UNIPHIER_I2C_MYAD 0x08 /* slave address */
# define UNIPHIER_I2C_CLK 0x0c /* clock frequency control */
# define UNIPHIER_I2C_BRST 0x10 /* bus reset */
# define UNIPHIER_I2C_BRST_FOEN BIT(1) /* normal operation */
# define UNIPHIER_I2C_BRST_RSCL BIT(0) /* release SCL */
# define UNIPHIER_I2C_HOLD 0x14 /* hold time control */
# define UNIPHIER_I2C_BSTS 0x18 /* bus status monitor */
# define UNIPHIER_I2C_BSTS_SDA BIT(1) /* readback of SDA line */
# define UNIPHIER_I2C_BSTS_SCL BIT(0) /* readback of SCL line */
# define UNIPHIER_I2C_NOISE 0x1c /* noise filter control */
# define UNIPHIER_I2C_SETUP 0x20 /* setup time control */
struct uniphier_i2c_priv {
struct completion comp ;
struct i2c_adapter adap ;
void __iomem * membase ;
struct clk * clk ;
unsigned int busy_cnt ;
2017-07-31 14:53:06 +09:00
unsigned int clk_cycle ;
2015-10-23 19:51:59 +09:00
} ;
static irqreturn_t uniphier_i2c_interrupt ( int irq , void * dev_id )
{
struct uniphier_i2c_priv * priv = dev_id ;
/*
* This hardware uses edge triggered interrupt . Do not touch the
* hardware registers in this handler to make sure to catch the next
* interrupt edge . Just send a complete signal and return .
*/
complete ( & priv - > comp ) ;
return IRQ_HANDLED ;
}
static int uniphier_i2c_xfer_byte ( struct i2c_adapter * adap , u32 txdata ,
u32 * rxdatap )
{
struct uniphier_i2c_priv * priv = i2c_get_adapdata ( adap ) ;
unsigned long time_left ;
u32 rxdata ;
reinit_completion ( & priv - > comp ) ;
txdata | = UNIPHIER_I2C_DTRM_IRQEN ;
writel ( txdata , priv - > membase + UNIPHIER_I2C_DTRM ) ;
time_left = wait_for_completion_timeout ( & priv - > comp , adap - > timeout ) ;
if ( unlikely ( ! time_left ) ) {
dev_err ( & adap - > dev , " transaction timeout \n " ) ;
return - ETIMEDOUT ;
}
rxdata = readl ( priv - > membase + UNIPHIER_I2C_DREC ) ;
if ( rxdatap )
* rxdatap = rxdata ;
return 0 ;
}
static int uniphier_i2c_send_byte ( struct i2c_adapter * adap , u32 txdata )
{
u32 rxdata ;
int ret ;
ret = uniphier_i2c_xfer_byte ( adap , txdata , & rxdata ) ;
if ( ret )
return ret ;
2019-09-05 13:46:48 +09:00
if ( unlikely ( rxdata & UNIPHIER_I2C_DREC_LAB ) )
2015-10-23 19:51:59 +09:00
return - EAGAIN ;
2019-09-05 13:46:48 +09:00
if ( unlikely ( rxdata & UNIPHIER_I2C_DREC_LRB ) )
2015-10-23 19:51:59 +09:00
return - ENXIO ;
return 0 ;
}
static int uniphier_i2c_tx ( struct i2c_adapter * adap , u16 addr , u16 len ,
const u8 * buf )
{
int ret ;
ret = uniphier_i2c_send_byte ( adap , addr < < 1 |
UNIPHIER_I2C_DTRM_STA |
UNIPHIER_I2C_DTRM_NACK ) ;
if ( ret )
return ret ;
while ( len - - ) {
ret = uniphier_i2c_send_byte ( adap ,
UNIPHIER_I2C_DTRM_NACK | * buf + + ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static int uniphier_i2c_rx ( struct i2c_adapter * adap , u16 addr , u16 len ,
u8 * buf )
{
int ret ;
ret = uniphier_i2c_send_byte ( adap , addr < < 1 |
UNIPHIER_I2C_DTRM_STA |
UNIPHIER_I2C_DTRM_NACK |
UNIPHIER_I2C_DTRM_RD ) ;
if ( ret )
return ret ;
while ( len - - ) {
u32 rxdata ;
ret = uniphier_i2c_xfer_byte ( adap ,
len ? 0 : UNIPHIER_I2C_DTRM_NACK ,
& rxdata ) ;
if ( ret )
return ret ;
* buf + + = rxdata ;
}
return 0 ;
}
static int uniphier_i2c_stop ( struct i2c_adapter * adap )
{
return uniphier_i2c_send_byte ( adap , UNIPHIER_I2C_DTRM_STO |
UNIPHIER_I2C_DTRM_NACK ) ;
}
static int uniphier_i2c_master_xfer_one ( struct i2c_adapter * adap ,
struct i2c_msg * msg , bool stop )
{
bool is_read = msg - > flags & I2C_M_RD ;
bool recovery = false ;
int ret ;
if ( is_read )
ret = uniphier_i2c_rx ( adap , msg - > addr , msg - > len , msg - > buf ) ;
else
ret = uniphier_i2c_tx ( adap , msg - > addr , msg - > len , msg - > buf ) ;
if ( ret = = - EAGAIN ) /* could not acquire bus. bail out without STOP */
return ret ;
if ( ret = = - ETIMEDOUT ) {
/* This error is fatal. Needs recovery. */
stop = false ;
recovery = true ;
}
if ( stop ) {
int ret2 = uniphier_i2c_stop ( adap ) ;
if ( ret2 ) {
/* Failed to issue STOP. The bus needs recovery. */
recovery = true ;
ret = ret ? : ret2 ;
}
}
if ( recovery )
i2c_recover_bus ( adap ) ;
return ret ;
}
static int uniphier_i2c_check_bus_busy ( struct i2c_adapter * adap )
{
struct uniphier_i2c_priv * priv = i2c_get_adapdata ( adap ) ;
if ( ! ( readl ( priv - > membase + UNIPHIER_I2C_DREC ) &
UNIPHIER_I2C_DREC_BBN ) ) {
if ( priv - > busy_cnt + + > 3 ) {
/*
* If bus busy continues too long , it is probably
* in a wrong state . Try bus recovery .
*/
i2c_recover_bus ( adap ) ;
priv - > busy_cnt = 0 ;
}
return - EAGAIN ;
}
priv - > busy_cnt = 0 ;
return 0 ;
}
static int uniphier_i2c_master_xfer ( struct i2c_adapter * adap ,
struct i2c_msg * msgs , int num )
{
struct i2c_msg * msg , * emsg = msgs + num ;
int ret ;
ret = uniphier_i2c_check_bus_busy ( adap ) ;
if ( ret )
return ret ;
for ( msg = msgs ; msg < emsg ; msg + + ) {
2018-08-31 23:30:47 +09:00
/* Emit STOP if it is the last message or I2C_M_STOP is set. */
bool stop = ( msg + 1 = = emsg ) | | ( msg - > flags & I2C_M_STOP ) ;
2015-10-23 19:51:59 +09:00
ret = uniphier_i2c_master_xfer_one ( adap , msg , stop ) ;
if ( ret )
return ret ;
}
return num ;
}
static u32 uniphier_i2c_functionality ( struct i2c_adapter * adap )
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL ;
}
static const struct i2c_algorithm uniphier_i2c_algo = {
. master_xfer = uniphier_i2c_master_xfer ,
. functionality = uniphier_i2c_functionality ,
} ;
static void uniphier_i2c_reset ( struct uniphier_i2c_priv * priv , bool reset_on )
{
u32 val = UNIPHIER_I2C_BRST_RSCL ;
val | = reset_on ? 0 : UNIPHIER_I2C_BRST_FOEN ;
writel ( val , priv - > membase + UNIPHIER_I2C_BRST ) ;
}
static int uniphier_i2c_get_scl ( struct i2c_adapter * adap )
{
struct uniphier_i2c_priv * priv = i2c_get_adapdata ( adap ) ;
return ! ! ( readl ( priv - > membase + UNIPHIER_I2C_BSTS ) &
UNIPHIER_I2C_BSTS_SCL ) ;
}
static void uniphier_i2c_set_scl ( struct i2c_adapter * adap , int val )
{
struct uniphier_i2c_priv * priv = i2c_get_adapdata ( adap ) ;
writel ( val ? UNIPHIER_I2C_BRST_RSCL : 0 ,
priv - > membase + UNIPHIER_I2C_BRST ) ;
}
static int uniphier_i2c_get_sda ( struct i2c_adapter * adap )
{
struct uniphier_i2c_priv * priv = i2c_get_adapdata ( adap ) ;
return ! ! ( readl ( priv - > membase + UNIPHIER_I2C_BSTS ) &
UNIPHIER_I2C_BSTS_SDA ) ;
}
static void uniphier_i2c_unprepare_recovery ( struct i2c_adapter * adap )
{
uniphier_i2c_reset ( i2c_get_adapdata ( adap ) , false ) ;
}
static struct i2c_bus_recovery_info uniphier_i2c_bus_recovery_info = {
. recover_bus = i2c_generic_scl_recovery ,
. get_scl = uniphier_i2c_get_scl ,
. set_scl = uniphier_i2c_set_scl ,
. get_sda = uniphier_i2c_get_sda ,
. unprepare_recovery = uniphier_i2c_unprepare_recovery ,
} ;
2017-07-31 14:53:06 +09:00
static void uniphier_i2c_hw_init ( struct uniphier_i2c_priv * priv )
2015-10-23 19:51:59 +09:00
{
2017-07-31 14:53:06 +09:00
unsigned int cyc = priv - > clk_cycle ;
2015-10-23 19:51:59 +09:00
uniphier_i2c_reset ( priv , true ) ;
2018-12-06 12:55:27 +09:00
/*
* Bit30 - 16 : clock cycles of tLOW .
* Standard - mode : tLOW = 4.7 us , tHIGH = 4.0 us
* Fast - mode : tLOW = 1.3 us , tHIGH = 0.6 us
* " tLow/tHIGH = 5/4 " meets both .
*/
writel ( ( cyc * 5 / 9 < < 16 ) | cyc , priv - > membase + UNIPHIER_I2C_CLK ) ;
2015-10-23 19:51:59 +09:00
uniphier_i2c_reset ( priv , false ) ;
}
static int uniphier_i2c_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct uniphier_i2c_priv * priv ;
2016-09-01 20:46:28 +09:00
u32 bus_speed ;
unsigned long clk_rate ;
int irq , ret ;
2015-10-23 19:51:59 +09:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2019-09-05 12:45:32 +09:00
priv - > membase = devm_platform_ioremap_resource ( pdev , 0 ) ;
2015-10-23 19:51:59 +09:00
if ( IS_ERR ( priv - > membase ) )
return PTR_ERR ( priv - > membase ) ;
irq = platform_get_irq ( pdev , 0 ) ;
2020-04-16 23:23:45 +08:00
if ( irq < 0 )
2015-10-23 19:51:59 +09:00
return irq ;
2016-09-01 20:46:28 +09:00
if ( of_property_read_u32 ( dev - > of_node , " clock-frequency " , & bus_speed ) )
2020-03-24 14:32:16 +02:00
bus_speed = I2C_MAX_STANDARD_MODE_FREQ ;
2016-09-01 20:46:28 +09:00
2020-03-24 14:32:16 +02:00
if ( ! bus_speed | | bus_speed > I2C_MAX_FAST_MODE_FREQ ) {
2016-09-01 20:46:28 +09:00
dev_err ( dev , " invalid clock-frequency %d \n " , bus_speed ) ;
return - EINVAL ;
}
2023-06-12 00:57:02 +02:00
priv - > clk = devm_clk_get_enabled ( dev , NULL ) ;
2016-09-01 20:46:28 +09:00
if ( IS_ERR ( priv - > clk ) ) {
2023-06-12 00:57:02 +02:00
dev_err ( dev , " failed to enable clock \n " ) ;
2016-09-01 20:46:28 +09:00
return PTR_ERR ( priv - > clk ) ;
}
clk_rate = clk_get_rate ( priv - > clk ) ;
if ( ! clk_rate ) {
dev_err ( dev , " input clock rate should not be zero \n " ) ;
2023-06-12 00:57:02 +02:00
return - EINVAL ;
2016-09-01 20:46:28 +09:00
}
2017-07-31 14:53:06 +09:00
priv - > clk_cycle = clk_rate / bus_speed ;
2015-10-23 19:51:59 +09:00
init_completion ( & priv - > comp ) ;
priv - > adap . owner = THIS_MODULE ;
priv - > adap . algo = & uniphier_i2c_algo ;
priv - > adap . dev . parent = dev ;
priv - > adap . dev . of_node = dev - > of_node ;
2022-08-11 09:10:30 +02:00
strscpy ( priv - > adap . name , " UniPhier I2C " , sizeof ( priv - > adap . name ) ) ;
2015-10-23 19:51:59 +09:00
priv - > adap . bus_recovery_info = & uniphier_i2c_bus_recovery_info ;
i2c_set_adapdata ( & priv - > adap , priv ) ;
platform_set_drvdata ( pdev , priv ) ;
2017-07-31 14:53:06 +09:00
uniphier_i2c_hw_init ( priv ) ;
2015-10-23 19:51:59 +09:00
ret = devm_request_irq ( dev , irq , uniphier_i2c_interrupt , 0 , pdev - > name ,
priv ) ;
if ( ret ) {
dev_err ( dev , " failed to request irq %d \n " , irq ) ;
2023-06-12 00:57:02 +02:00
return ret ;
2015-10-23 19:51:59 +09:00
}
2023-06-12 00:57:02 +02:00
return i2c_add_adapter ( & priv - > adap ) ;
2015-10-23 19:51:59 +09:00
}
2023-05-08 22:51:38 +02:00
static void uniphier_i2c_remove ( struct platform_device * pdev )
2015-10-23 19:51:59 +09:00
{
struct uniphier_i2c_priv * priv = platform_get_drvdata ( pdev ) ;
i2c_del_adapter ( & priv - > adap ) ;
}
2017-07-31 14:53:06 +09:00
static int __maybe_unused uniphier_i2c_suspend ( struct device * dev )
{
struct uniphier_i2c_priv * priv = dev_get_drvdata ( dev ) ;
clk_disable_unprepare ( priv - > clk ) ;
return 0 ;
}
static int __maybe_unused uniphier_i2c_resume ( struct device * dev )
{
struct uniphier_i2c_priv * priv = dev_get_drvdata ( dev ) ;
int ret ;
ret = clk_prepare_enable ( priv - > clk ) ;
if ( ret )
return ret ;
uniphier_i2c_hw_init ( priv ) ;
return 0 ;
}
static const struct dev_pm_ops uniphier_i2c_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( uniphier_i2c_suspend , uniphier_i2c_resume )
} ;
2015-10-23 19:51:59 +09:00
static const struct of_device_id uniphier_i2c_match [ ] = {
{ . compatible = " socionext,uniphier-i2c " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , uniphier_i2c_match ) ;
static struct platform_driver uniphier_i2c_drv = {
. probe = uniphier_i2c_probe ,
2023-05-08 22:51:38 +02:00
. remove_new = uniphier_i2c_remove ,
2015-10-23 19:51:59 +09:00
. driver = {
. name = " uniphier-i2c " ,
. of_match_table = uniphier_i2c_match ,
2017-07-31 14:53:06 +09:00
. pm = & uniphier_i2c_pm_ops ,
2015-10-23 19:51:59 +09:00
} ,
} ;
module_platform_driver ( uniphier_i2c_drv ) ;
MODULE_AUTHOR ( " Masahiro Yamada <yamada.masahiro@socionext.com> " ) ;
MODULE_DESCRIPTION ( " UniPhier I2C bus driver " ) ;
MODULE_LICENSE ( " GPL " ) ;