2018-08-10 13:26:49 +03:00
// SPDX-License-Identifier: GPL-2.0
2017-06-22 11:17:32 +01:00
/*
* Synopsys DesignWare I2C adapter driver ( slave only ) .
*
* Based on the Synopsys DesignWare I2C adapter driver ( master ) .
*
* Copyright ( C ) 2016 Synopsys Inc .
*/
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/errno.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/pm_runtime.h>
2020-05-28 12:33:18 +03:00
# include <linux/regmap.h>
2017-06-22 11:17:32 +01:00
# include "i2c-designware-core.h"
static void i2c_dw_configure_fifo_slave ( struct dw_i2c_dev * dev )
{
/* Configure Tx/Rx FIFO threshold levels. */
2020-05-28 12:33:18 +03:00
regmap_write ( dev - > map , DW_IC_TX_TL , 0 ) ;
regmap_write ( dev - > map , DW_IC_RX_TL , 0 ) ;
2017-06-22 11:17:32 +01:00
/* Configure the I2C slave. */
2020-05-28 12:33:18 +03:00
regmap_write ( dev - > map , DW_IC_CON , dev - > slave_cfg ) ;
regmap_write ( dev - > map , DW_IC_INTR_MASK , DW_IC_INTR_SLAVE_MASK ) ;
2017-06-22 11:17:32 +01:00
}
/**
* i2c_dw_init_slave ( ) - Initialize the designware i2c slave hardware
* @ dev : device private data
*
* This function configures and enables the I2C in slave mode .
* This function is called during I2C init function , and in case of timeout at
* run time .
*/
2017-06-28 17:23:28 +03:00
static int i2c_dw_init_slave ( struct dw_i2c_dev * dev )
2017-06-22 11:17:32 +01:00
{
int ret ;
ret = i2c_dw_acquire_lock ( dev ) ;
if ( ret )
return ret ;
/* Disable the adapter. */
2018-04-28 16:56:07 +03:00
__i2c_dw_disable ( dev ) ;
2017-06-22 11:17:32 +01:00
2018-06-19 14:23:22 +03:00
/* Write SDA hold time if supported */
if ( dev - > sda_hold_time )
2020-05-28 12:33:18 +03:00
regmap_write ( dev - > map , DW_IC_SDA_HOLD , dev - > sda_hold_time ) ;
2017-06-22 11:17:32 +01:00
i2c_dw_configure_fifo_slave ( dev ) ;
i2c_dw_release_lock ( dev ) ;
return 0 ;
}
static int i2c_dw_reg_slave ( struct i2c_client * slave )
{
struct dw_i2c_dev * dev = i2c_get_adapdata ( slave - > adapter ) ;
if ( dev - > slave )
return - EBUSY ;
if ( slave - > flags & I2C_CLIENT_TEN )
return - EAFNOSUPPORT ;
2017-08-15 17:34:45 +03:00
pm_runtime_get_sync ( dev - > dev ) ;
2017-06-22 11:17:32 +01:00
/*
* Set slave address in the IC_SAR register ,
* the address to which the DW_apb_i2c responds .
*/
2018-04-28 16:56:07 +03:00
__i2c_dw_disable_nowait ( dev ) ;
2020-05-28 12:33:18 +03:00
regmap_write ( dev - > map , DW_IC_SAR , slave - > addr ) ;
2017-06-22 11:17:32 +01:00
dev - > slave = slave ;
2018-04-28 16:56:07 +03:00
__i2c_dw_enable ( dev ) ;
2017-06-22 11:17:32 +01:00
2022-11-07 15:42:39 +02:00
dev - > status = 0 ;
2017-06-22 11:17:32 +01:00
return 0 ;
}
static int i2c_dw_unreg_slave ( struct i2c_client * slave )
{
struct dw_i2c_dev * dev = i2c_get_adapdata ( slave - > adapter ) ;
2022-11-07 15:42:46 +02:00
regmap_write ( dev - > map , DW_IC_INTR_MASK , 0 ) ;
2017-06-22 11:17:32 +01:00
dev - > disable ( dev ) ;
2019-08-15 16:52:11 +03:00
synchronize_irq ( dev - > irq ) ;
2017-06-22 11:17:32 +01:00
dev - > slave = NULL ;
2017-08-15 17:34:45 +03:00
pm_runtime_put ( dev - > dev ) ;
2017-06-22 11:17:32 +01:00
return 0 ;
}
static u32 i2c_dw_read_clear_intrbits_slave ( struct dw_i2c_dev * dev )
{
2023-01-24 17:17:32 +05:30
unsigned int stat , dummy ;
2017-06-22 11:17:32 +01:00
/*
* The IC_INTR_STAT register just indicates " enabled " interrupts .
2020-03-19 17:30:12 +02:00
* The unmasked raw version of interrupt status bits is available
2017-06-22 11:17:32 +01:00
* in the IC_RAW_INTR_STAT register .
*
* That is ,
2020-05-28 12:33:18 +03:00
* stat = readl ( IC_INTR_STAT ) ;
2017-06-22 11:17:32 +01:00
* equals to ,
2020-05-28 12:33:18 +03:00
* stat = readl ( IC_RAW_INTR_STAT ) & readl ( IC_INTR_MASK ) ;
2017-06-22 11:17:32 +01:00
*
* The raw version might be useful for debugging purposes .
*/
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_INTR_STAT , & stat ) ;
2017-06-22 11:17:32 +01:00
/*
* Do not use the IC_CLR_INTR register to clear interrupts , or
* you ' ll miss some interrupts , triggered during the period from
2020-05-28 12:33:18 +03:00
* readl ( IC_INTR_STAT ) to readl ( IC_CLR_INTR ) .
2017-06-22 11:17:32 +01:00
*
* Instead , use the separately - prepared IC_CLR_ * registers .
*/
if ( stat & DW_IC_INTR_TX_ABRT )
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_CLR_TX_ABRT , & dummy ) ;
2017-06-22 11:17:32 +01:00
if ( stat & DW_IC_INTR_RX_UNDER )
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_CLR_RX_UNDER , & dummy ) ;
2017-06-22 11:17:32 +01:00
if ( stat & DW_IC_INTR_RX_OVER )
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_CLR_RX_OVER , & dummy ) ;
2017-06-22 11:17:32 +01:00
if ( stat & DW_IC_INTR_TX_OVER )
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_CLR_TX_OVER , & dummy ) ;
2017-06-22 11:17:32 +01:00
if ( stat & DW_IC_INTR_RX_DONE )
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_CLR_RX_DONE , & dummy ) ;
2017-06-22 11:17:32 +01:00
if ( stat & DW_IC_INTR_ACTIVITY )
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_CLR_ACTIVITY , & dummy ) ;
2017-06-22 11:17:32 +01:00
if ( stat & DW_IC_INTR_STOP_DET )
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_CLR_STOP_DET , & dummy ) ;
2017-06-22 11:17:32 +01:00
if ( stat & DW_IC_INTR_START_DET )
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_CLR_START_DET , & dummy ) ;
2017-06-22 11:17:32 +01:00
if ( stat & DW_IC_INTR_GEN_CALL )
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_CLR_GEN_CALL , & dummy ) ;
2017-06-22 11:17:32 +01:00
return stat ;
}
/*
* Interrupt service routine . This gets called whenever an I2C slave interrupt
* occurs .
*/
2022-11-07 15:42:42 +02:00
static irqreturn_t i2c_dw_isr_slave ( int this_irq , void * dev_id )
2017-06-22 11:17:32 +01:00
{
2022-11-07 15:42:42 +02:00
struct dw_i2c_dev * dev = dev_id ;
2023-01-24 17:17:32 +05:30
unsigned int raw_stat , stat , enabled , tmp ;
2020-05-28 12:33:18 +03:00
u8 val = 0 , slave_activity ;
2017-06-22 11:17:32 +01:00
2020-05-28 12:33:18 +03:00
regmap_read ( dev - > map , DW_IC_ENABLE , & enabled ) ;
regmap_read ( dev - > map , DW_IC_RAW_INTR_STAT , & raw_stat ) ;
regmap_read ( dev - > map , DW_IC_STATUS , & tmp ) ;
slave_activity = ( ( tmp & DW_IC_STATUS_SLAVE_ACTIVITY ) > > 6 ) ;
2017-06-22 11:17:32 +01:00
2017-08-11 14:44:55 +03:00
if ( ! enabled | | ! ( raw_stat & ~ DW_IC_INTR_ACTIVITY ) | | ! dev - > slave )
2022-11-07 15:42:42 +02:00
return IRQ_NONE ;
2017-06-22 11:17:32 +01:00
2020-10-30 16:04:19 +08:00
stat = i2c_dw_read_clear_intrbits_slave ( dev ) ;
2017-06-22 11:17:32 +01:00
dev_dbg ( dev - > dev ,
2017-06-29 09:22:15 +01:00
" %#x STATUS SLAVE_ACTIVITY=%#x : RAW_INTR_STAT=%#x : INTR_STAT=%#x \n " ,
2017-06-22 11:17:32 +01:00
enabled , slave_activity , raw_stat , stat ) ;
2020-10-30 16:04:20 +08:00
if ( stat & DW_IC_INTR_RX_FULL ) {
2022-11-07 15:42:37 +02:00
if ( ! ( dev - > status & STATUS_WRITE_IN_PROGRESS ) ) {
dev - > status | = STATUS_WRITE_IN_PROGRESS ;
dev - > status & = ~ STATUS_READ_IN_PROGRESS ;
2020-10-30 16:04:20 +08:00
i2c_slave_event ( dev - > slave , I2C_SLAVE_WRITE_REQUESTED ,
& val ) ;
}
2022-11-07 15:42:38 +02:00
do {
regmap_read ( dev - > map , DW_IC_DATA_CMD , & tmp ) ;
2023-05-24 11:14:59 -07:00
if ( tmp & DW_IC_DATA_CMD_FIRST_DATA_BYTE )
i2c_slave_event ( dev - > slave ,
I2C_SLAVE_WRITE_REQUESTED ,
& val ) ;
2022-11-07 15:42:38 +02:00
val = tmp ;
i2c_slave_event ( dev - > slave , I2C_SLAVE_WRITE_RECEIVED ,
& val ) ;
regmap_read ( dev - > map , DW_IC_STATUS , & tmp ) ;
} while ( tmp & DW_IC_STATUS_RFNE ) ;
2020-10-30 16:04:20 +08:00
}
2017-06-22 11:17:32 +01:00
if ( stat & DW_IC_INTR_RD_REQ ) {
if ( slave_activity ) {
2020-10-30 16:04:20 +08:00
regmap_read ( dev - > map , DW_IC_CLR_RD_REQ , & tmp ) ;
2022-11-07 15:42:37 +02:00
if ( ! ( dev - > status & STATUS_READ_IN_PROGRESS ) ) {
i2c_slave_event ( dev - > slave ,
I2C_SLAVE_READ_REQUESTED ,
& val ) ;
dev - > status | = STATUS_READ_IN_PROGRESS ;
dev - > status & = ~ STATUS_WRITE_IN_PROGRESS ;
} else {
i2c_slave_event ( dev - > slave ,
I2C_SLAVE_READ_PROCESSED ,
& val ) ;
}
regmap_write ( dev - > map , DW_IC_DATA_CMD , val ) ;
2017-06-22 11:17:32 +01:00
}
}
2022-11-07 15:42:37 +02:00
if ( stat & DW_IC_INTR_STOP_DET )
2017-06-22 11:17:32 +01:00
i2c_slave_event ( dev - > slave , I2C_SLAVE_STOP , & val ) ;
2022-11-07 15:42:42 +02:00
return IRQ_HANDLED ;
2017-06-22 11:17:32 +01:00
}
2017-07-09 15:57:44 -05:00
static const struct i2c_algorithm i2c_dw_algo = {
2017-06-22 11:17:32 +01:00
. functionality = i2c_dw_func ,
. reg_slave = i2c_dw_reg_slave ,
. unreg_slave = i2c_dw_unreg_slave ,
} ;
2020-04-25 16:44:45 +03:00
void i2c_dw_configure_slave ( struct dw_i2c_dev * dev )
{
dev - > functionality = I2C_FUNC_SLAVE | DW_IC_DEFAULT_FUNCTIONALITY ;
dev - > slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |
DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED ;
dev - > mode = DW_IC_SLAVE ;
}
EXPORT_SYMBOL_GPL ( i2c_dw_configure_slave ) ;
2017-06-22 11:17:32 +01:00
int i2c_dw_probe_slave ( struct dw_i2c_dev * dev )
{
struct i2c_adapter * adap = & dev - > adapter ;
int ret ;
dev - > init = i2c_dw_init_slave ;
dev - > disable = i2c_dw_disable ;
2020-05-28 12:33:18 +03:00
ret = i2c_dw_init_regmap ( dev ) ;
2018-06-19 14:23:19 +03:00
if ( ret )
return ret ;
2018-06-19 14:23:22 +03:00
ret = i2c_dw_set_sda_hold ( dev ) ;
if ( ret )
return ret ;
2020-05-28 12:33:18 +03:00
ret = i2c_dw_set_fifo_size ( dev ) ;
if ( ret )
return ret ;
2020-03-06 16:19:54 +03:00
2017-06-22 11:17:32 +01:00
ret = dev - > init ( dev ) ;
if ( ret )
return ret ;
snprintf ( adap - > name , sizeof ( adap - > name ) ,
" Synopsys DesignWare I2C Slave adapter " ) ;
adap - > retries = 3 ;
adap - > algo = & i2c_dw_algo ;
adap - > dev . parent = dev - > dev ;
i2c_set_adapdata ( adap , dev ) ;
ret = devm_request_irq ( dev - > dev , dev - > irq , i2c_dw_isr_slave ,
IRQF_SHARED , dev_name ( dev - > dev ) , dev ) ;
if ( ret ) {
dev_err ( dev - > dev , " failure requesting irq %i: %d \n " ,
dev - > irq , ret ) ;
return ret ;
}
ret = i2c_add_numbered_adapter ( adap ) ;
if ( ret )
dev_err ( dev - > dev , " failure adding adapter: %d \n " , ret ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( i2c_dw_probe_slave ) ;
MODULE_AUTHOR ( " Luis Oliveira <lolivei@synopsys.com> " ) ;
MODULE_DESCRIPTION ( " Synopsys DesignWare I2C bus slave adapter " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;