2018-05-30 22:35:55 +03:00
// SPDX-License-Identifier: GPL-2.0+
2016-11-30 05:40:47 +03:00
/*
* This is i . MX low power i2c controller driver .
*
* Copyright 2016 Freescale Semiconductor , Inc .
*/
# include <linux/clk.h>
# include <linux/completion.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/errno.h>
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
2017-01-17 13:20:55 +03:00
# include <linux/pinctrl/consumer.h>
2016-11-30 05:40:47 +03:00
# include <linux/platform_device.h>
2018-01-02 12:11:52 +03:00
# include <linux/pm_runtime.h>
2016-11-30 05:40:47 +03:00
# include <linux/sched.h>
# include <linux/slab.h>
# define DRIVER_NAME "imx-lpi2c"
# define LPI2C_PARAM 0x04 /* i2c RX/TX FIFO size */
# define LPI2C_MCR 0x10 /* i2c contrl register */
# define LPI2C_MSR 0x14 /* i2c status register */
# define LPI2C_MIER 0x18 /* i2c interrupt enable */
# define LPI2C_MCFGR0 0x20 /* i2c master configuration */
# define LPI2C_MCFGR1 0x24 /* i2c master configuration */
# define LPI2C_MCFGR2 0x28 /* i2c master configuration */
# define LPI2C_MCFGR3 0x2C /* i2c master configuration */
# define LPI2C_MCCR0 0x48 /* i2c master clk configuration */
# define LPI2C_MCCR1 0x50 /* i2c master clk configuration */
# define LPI2C_MFCR 0x58 /* i2c master FIFO control */
# define LPI2C_MFSR 0x5C /* i2c master FIFO status */
# define LPI2C_MTDR 0x60 /* i2c master TX data register */
# define LPI2C_MRDR 0x70 /* i2c master RX data register */
/* i2c command */
# define TRAN_DATA 0X00
# define RECV_DATA 0X01
# define GEN_STOP 0X02
# define RECV_DISCARD 0X03
# define GEN_START 0X04
# define START_NACK 0X05
# define START_HIGH 0X06
# define START_HIGH_NACK 0X07
# define MCR_MEN BIT(0)
# define MCR_RST BIT(1)
# define MCR_DOZEN BIT(2)
# define MCR_DBGEN BIT(3)
# define MCR_RTF BIT(8)
# define MCR_RRF BIT(9)
# define MSR_TDF BIT(0)
# define MSR_RDF BIT(1)
# define MSR_SDF BIT(9)
# define MSR_NDF BIT(10)
# define MSR_ALF BIT(11)
# define MSR_MBF BIT(24)
# define MSR_BBF BIT(25)
# define MIER_TDIE BIT(0)
# define MIER_RDIE BIT(1)
# define MIER_SDIE BIT(9)
# define MIER_NDIE BIT(10)
# define MCFGR1_AUTOSTOP BIT(8)
# define MCFGR1_IGNACK BIT(9)
# define MRDR_RXEMPTY BIT(14)
# define I2C_CLK_RATIO 2
# define CHUNK_DATA 256
# define LPI2C_DEFAULT_RATE 100000
# define STARDARD_MAX_BITRATE 400000
# define FAST_MAX_BITRATE 1000000
# define FAST_PLUS_MAX_BITRATE 3400000
# define HIGHSPEED_MAX_BITRATE 5000000
2018-01-02 12:11:52 +03:00
# define I2C_PM_TIMEOUT 10 /* ms */
2016-11-30 05:40:47 +03:00
enum lpi2c_imx_mode {
STANDARD , /* 100+Kbps */
FAST , /* 400+Kbps */
FAST_PLUS , /* 1.0+Mbps */
HS , /* 3.4+Mbps */
ULTRA_FAST , /* 5.0+Mbps */
} ;
enum lpi2c_imx_pincfg {
TWO_PIN_OD ,
TWO_PIN_OO ,
TWO_PIN_PP ,
FOUR_PIN_PP ,
} ;
struct lpi2c_imx_struct {
struct i2c_adapter adapter ;
struct clk * clk ;
void __iomem * base ;
__u8 * rx_buf ;
__u8 * tx_buf ;
struct completion complete ;
unsigned int msglen ;
unsigned int delivered ;
unsigned int block_data ;
unsigned int bitrate ;
2016-12-02 06:38:16 +03:00
unsigned int txfifosize ;
unsigned int rxfifosize ;
2016-11-30 05:40:47 +03:00
enum lpi2c_imx_mode mode ;
} ;
static void lpi2c_imx_intctrl ( struct lpi2c_imx_struct * lpi2c_imx ,
unsigned int enable )
{
writel ( enable , lpi2c_imx - > base + LPI2C_MIER ) ;
}
static int lpi2c_imx_bus_busy ( struct lpi2c_imx_struct * lpi2c_imx )
{
unsigned long orig_jiffies = jiffies ;
unsigned int temp ;
while ( 1 ) {
temp = readl ( lpi2c_imx - > base + LPI2C_MSR ) ;
/* check for arbitration lost, clear if set */
if ( temp & MSR_ALF ) {
writel ( temp , lpi2c_imx - > base + LPI2C_MSR ) ;
return - EAGAIN ;
}
if ( temp & ( MSR_BBF | MSR_MBF ) )
break ;
if ( time_after ( jiffies , orig_jiffies + msecs_to_jiffies ( 500 ) ) ) {
dev_dbg ( & lpi2c_imx - > adapter . dev , " bus not work \n " ) ;
return - ETIMEDOUT ;
}
schedule ( ) ;
}
return 0 ;
}
static void lpi2c_imx_set_mode ( struct lpi2c_imx_struct * lpi2c_imx )
{
unsigned int bitrate = lpi2c_imx - > bitrate ;
enum lpi2c_imx_mode mode ;
if ( bitrate < STARDARD_MAX_BITRATE )
mode = STANDARD ;
else if ( bitrate < FAST_MAX_BITRATE )
mode = FAST ;
else if ( bitrate < FAST_PLUS_MAX_BITRATE )
mode = FAST_PLUS ;
else if ( bitrate < HIGHSPEED_MAX_BITRATE )
mode = HS ;
else
mode = ULTRA_FAST ;
lpi2c_imx - > mode = mode ;
}
static int lpi2c_imx_start ( struct lpi2c_imx_struct * lpi2c_imx ,
struct i2c_msg * msgs )
{
unsigned int temp ;
temp = readl ( lpi2c_imx - > base + LPI2C_MCR ) ;
temp | = MCR_RRF | MCR_RTF ;
writel ( temp , lpi2c_imx - > base + LPI2C_MCR ) ;
writel ( 0x7f00 , lpi2c_imx - > base + LPI2C_MSR ) ;
2018-05-16 10:16:47 +03:00
temp = i2c_8bit_addr_from_msg ( msgs ) | ( GEN_START < < 8 ) ;
2016-11-30 05:40:47 +03:00
writel ( temp , lpi2c_imx - > base + LPI2C_MTDR ) ;
return lpi2c_imx_bus_busy ( lpi2c_imx ) ;
}
static void lpi2c_imx_stop ( struct lpi2c_imx_struct * lpi2c_imx )
{
unsigned long orig_jiffies = jiffies ;
unsigned int temp ;
writel ( GEN_STOP < < 8 , lpi2c_imx - > base + LPI2C_MTDR ) ;
do {
temp = readl ( lpi2c_imx - > base + LPI2C_MSR ) ;
if ( temp & MSR_SDF )
break ;
if ( time_after ( jiffies , orig_jiffies + msecs_to_jiffies ( 500 ) ) ) {
dev_dbg ( & lpi2c_imx - > adapter . dev , " stop timeout \n " ) ;
break ;
}
schedule ( ) ;
} while ( 1 ) ;
}
/* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2 */
static int lpi2c_imx_config ( struct lpi2c_imx_struct * lpi2c_imx )
{
u8 prescale , filt , sethold , clkhi , clklo , datavd ;
unsigned int clk_rate , clk_cycle ;
enum lpi2c_imx_pincfg pincfg ;
unsigned int temp ;
lpi2c_imx_set_mode ( lpi2c_imx ) ;
clk_rate = clk_get_rate ( lpi2c_imx - > clk ) ;
if ( lpi2c_imx - > mode = = HS | | lpi2c_imx - > mode = = ULTRA_FAST )
filt = 0 ;
else
filt = 2 ;
for ( prescale = 0 ; prescale < = 7 ; prescale + + ) {
clk_cycle = clk_rate / ( ( 1 < < prescale ) * lpi2c_imx - > bitrate )
- 3 - ( filt > > 1 ) ;
clkhi = ( clk_cycle + I2C_CLK_RATIO ) / ( I2C_CLK_RATIO + 1 ) ;
clklo = clk_cycle - clkhi ;
if ( clklo < 64 )
break ;
}
if ( prescale > 7 )
return - EINVAL ;
/* set MCFGR1: PINCFG, PRESCALE, IGNACK */
if ( lpi2c_imx - > mode = = ULTRA_FAST )
pincfg = TWO_PIN_OO ;
else
pincfg = TWO_PIN_OD ;
temp = prescale | pincfg < < 24 ;
if ( lpi2c_imx - > mode = = ULTRA_FAST )
temp | = MCFGR1_IGNACK ;
writel ( temp , lpi2c_imx - > base + LPI2C_MCFGR1 ) ;
/* set MCFGR2: FILTSDA, FILTSCL */
temp = ( filt < < 16 ) | ( filt < < 24 ) ;
writel ( temp , lpi2c_imx - > base + LPI2C_MCFGR2 ) ;
/* set MCCR: DATAVD, SETHOLD, CLKHI, CLKLO */
sethold = clkhi ;
datavd = clkhi > > 1 ;
temp = datavd < < 24 | sethold < < 16 | clkhi < < 8 | clklo ;
if ( lpi2c_imx - > mode = = HS )
writel ( temp , lpi2c_imx - > base + LPI2C_MCCR1 ) ;
else
writel ( temp , lpi2c_imx - > base + LPI2C_MCCR0 ) ;
return 0 ;
}
static int lpi2c_imx_master_enable ( struct lpi2c_imx_struct * lpi2c_imx )
{
unsigned int temp ;
int ret ;
2018-01-02 12:11:52 +03:00
ret = pm_runtime_get_sync ( lpi2c_imx - > adapter . dev . parent ) ;
if ( ret < 0 )
2016-11-30 05:40:47 +03:00
return ret ;
temp = MCR_RST ;
writel ( temp , lpi2c_imx - > base + LPI2C_MCR ) ;
writel ( 0 , lpi2c_imx - > base + LPI2C_MCR ) ;
ret = lpi2c_imx_config ( lpi2c_imx ) ;
if ( ret )
2018-01-02 12:11:52 +03:00
goto rpm_put ;
2016-11-30 05:40:47 +03:00
temp = readl ( lpi2c_imx - > base + LPI2C_MCR ) ;
temp | = MCR_MEN ;
writel ( temp , lpi2c_imx - > base + LPI2C_MCR ) ;
return 0 ;
2018-01-02 12:11:52 +03:00
rpm_put :
pm_runtime_mark_last_busy ( lpi2c_imx - > adapter . dev . parent ) ;
pm_runtime_put_autosuspend ( lpi2c_imx - > adapter . dev . parent ) ;
2016-11-30 05:40:47 +03:00
return ret ;
}
static int lpi2c_imx_master_disable ( struct lpi2c_imx_struct * lpi2c_imx )
{
u32 temp ;
temp = readl ( lpi2c_imx - > base + LPI2C_MCR ) ;
temp & = ~ MCR_MEN ;
writel ( temp , lpi2c_imx - > base + LPI2C_MCR ) ;
2018-01-02 12:11:52 +03:00
pm_runtime_mark_last_busy ( lpi2c_imx - > adapter . dev . parent ) ;
pm_runtime_put_autosuspend ( lpi2c_imx - > adapter . dev . parent ) ;
2016-11-30 05:40:47 +03:00
return 0 ;
}
static int lpi2c_imx_msg_complete ( struct lpi2c_imx_struct * lpi2c_imx )
{
unsigned long timeout ;
timeout = wait_for_completion_timeout ( & lpi2c_imx - > complete , HZ ) ;
return timeout ? 0 : - ETIMEDOUT ;
}
static int lpi2c_imx_txfifo_empty ( struct lpi2c_imx_struct * lpi2c_imx )
{
unsigned long orig_jiffies = jiffies ;
u32 txcnt ;
do {
txcnt = readl ( lpi2c_imx - > base + LPI2C_MFSR ) & 0xff ;
if ( readl ( lpi2c_imx - > base + LPI2C_MSR ) & MSR_NDF ) {
dev_dbg ( & lpi2c_imx - > adapter . dev , " NDF detected \n " ) ;
return - EIO ;
}
if ( time_after ( jiffies , orig_jiffies + msecs_to_jiffies ( 500 ) ) ) {
dev_dbg ( & lpi2c_imx - > adapter . dev , " txfifo empty timeout \n " ) ;
return - ETIMEDOUT ;
}
schedule ( ) ;
} while ( txcnt ) ;
return 0 ;
}
static void lpi2c_imx_set_tx_watermark ( struct lpi2c_imx_struct * lpi2c_imx )
{
2016-12-02 06:38:16 +03:00
writel ( lpi2c_imx - > txfifosize > > 1 , lpi2c_imx - > base + LPI2C_MFCR ) ;
2016-11-30 05:40:47 +03:00
}
static void lpi2c_imx_set_rx_watermark ( struct lpi2c_imx_struct * lpi2c_imx )
{
unsigned int temp , remaining ;
remaining = lpi2c_imx - > msglen - lpi2c_imx - > delivered ;
2016-12-02 06:38:16 +03:00
if ( remaining > ( lpi2c_imx - > rxfifosize > > 1 ) )
temp = lpi2c_imx - > rxfifosize > > 1 ;
2016-11-30 05:40:47 +03:00
else
temp = 0 ;
writel ( temp < < 16 , lpi2c_imx - > base + LPI2C_MFCR ) ;
}
static void lpi2c_imx_write_txfifo ( struct lpi2c_imx_struct * lpi2c_imx )
{
unsigned int data , txcnt ;
txcnt = readl ( lpi2c_imx - > base + LPI2C_MFSR ) & 0xff ;
2016-12-02 06:38:16 +03:00
while ( txcnt < lpi2c_imx - > txfifosize ) {
2016-11-30 05:40:47 +03:00
if ( lpi2c_imx - > delivered = = lpi2c_imx - > msglen )
break ;
data = lpi2c_imx - > tx_buf [ lpi2c_imx - > delivered + + ] ;
writel ( data , lpi2c_imx - > base + LPI2C_MTDR ) ;
txcnt + + ;
}
if ( lpi2c_imx - > delivered < lpi2c_imx - > msglen )
lpi2c_imx_intctrl ( lpi2c_imx , MIER_TDIE | MIER_NDIE ) ;
else
complete ( & lpi2c_imx - > complete ) ;
}
static void lpi2c_imx_read_rxfifo ( struct lpi2c_imx_struct * lpi2c_imx )
{
unsigned int blocklen , remaining ;
unsigned int temp , data ;
do {
data = readl ( lpi2c_imx - > base + LPI2C_MRDR ) ;
if ( data & MRDR_RXEMPTY )
break ;
lpi2c_imx - > rx_buf [ lpi2c_imx - > delivered + + ] = data & 0xff ;
} while ( 1 ) ;
/*
* First byte is the length of remaining packet in the SMBus block
* data read . Add it to msgs - > len .
*/
if ( lpi2c_imx - > block_data ) {
blocklen = lpi2c_imx - > rx_buf [ 0 ] ;
lpi2c_imx - > msglen + = blocklen ;
}
remaining = lpi2c_imx - > msglen - lpi2c_imx - > delivered ;
if ( ! remaining ) {
complete ( & lpi2c_imx - > complete ) ;
return ;
}
/* not finished, still waiting for rx data */
lpi2c_imx_set_rx_watermark ( lpi2c_imx ) ;
/* multiple receive commands */
if ( lpi2c_imx - > block_data ) {
lpi2c_imx - > block_data = 0 ;
temp = remaining ;
temp | = ( RECV_DATA < < 8 ) ;
writel ( temp , lpi2c_imx - > base + LPI2C_MTDR ) ;
} else if ( ! ( lpi2c_imx - > delivered & 0xff ) ) {
temp = ( remaining > CHUNK_DATA ? CHUNK_DATA : remaining ) - 1 ;
temp | = ( RECV_DATA < < 8 ) ;
writel ( temp , lpi2c_imx - > base + LPI2C_MTDR ) ;
}
lpi2c_imx_intctrl ( lpi2c_imx , MIER_RDIE ) ;
}
static void lpi2c_imx_write ( struct lpi2c_imx_struct * lpi2c_imx ,
struct i2c_msg * msgs )
{
lpi2c_imx - > tx_buf = msgs - > buf ;
lpi2c_imx_set_tx_watermark ( lpi2c_imx ) ;
lpi2c_imx_write_txfifo ( lpi2c_imx ) ;
}
static void lpi2c_imx_read ( struct lpi2c_imx_struct * lpi2c_imx ,
struct i2c_msg * msgs )
{
unsigned int temp ;
lpi2c_imx - > rx_buf = msgs - > buf ;
lpi2c_imx - > block_data = msgs - > flags & I2C_M_RECV_LEN ;
lpi2c_imx_set_rx_watermark ( lpi2c_imx ) ;
temp = msgs - > len > CHUNK_DATA ? CHUNK_DATA - 1 : msgs - > len - 1 ;
temp | = ( RECV_DATA < < 8 ) ;
writel ( temp , lpi2c_imx - > base + LPI2C_MTDR ) ;
lpi2c_imx_intctrl ( lpi2c_imx , MIER_RDIE | MIER_NDIE ) ;
}
static int lpi2c_imx_xfer ( struct i2c_adapter * adapter ,
struct i2c_msg * msgs , int num )
{
struct lpi2c_imx_struct * lpi2c_imx = i2c_get_adapdata ( adapter ) ;
unsigned int temp ;
int i , result ;
result = lpi2c_imx_master_enable ( lpi2c_imx ) ;
if ( result )
return result ;
for ( i = 0 ; i < num ; i + + ) {
result = lpi2c_imx_start ( lpi2c_imx , & msgs [ i ] ) ;
if ( result )
goto disable ;
/* quick smbus */
if ( num = = 1 & & msgs [ 0 ] . len = = 0 )
goto stop ;
lpi2c_imx - > delivered = 0 ;
lpi2c_imx - > msglen = msgs [ i ] . len ;
init_completion ( & lpi2c_imx - > complete ) ;
if ( msgs [ i ] . flags & I2C_M_RD )
lpi2c_imx_read ( lpi2c_imx , & msgs [ i ] ) ;
else
lpi2c_imx_write ( lpi2c_imx , & msgs [ i ] ) ;
result = lpi2c_imx_msg_complete ( lpi2c_imx ) ;
if ( result )
goto stop ;
if ( ! ( msgs [ i ] . flags & I2C_M_RD ) ) {
result = lpi2c_imx_txfifo_empty ( lpi2c_imx ) ;
if ( result )
goto stop ;
}
}
stop :
lpi2c_imx_stop ( lpi2c_imx ) ;
temp = readl ( lpi2c_imx - > base + LPI2C_MSR ) ;
if ( ( temp & MSR_NDF ) & & ! result )
result = - EIO ;
disable :
lpi2c_imx_master_disable ( lpi2c_imx ) ;
dev_dbg ( & lpi2c_imx - > adapter . dev , " <%s> exit with: %s: %d \n " , __func__ ,
( result < 0 ) ? " error " : " success msg " ,
( result < 0 ) ? result : num ) ;
return ( result < 0 ) ? result : num ;
}
static irqreturn_t lpi2c_imx_isr ( int irq , void * dev_id )
{
struct lpi2c_imx_struct * lpi2c_imx = dev_id ;
unsigned int temp ;
lpi2c_imx_intctrl ( lpi2c_imx , 0 ) ;
temp = readl ( lpi2c_imx - > base + LPI2C_MSR ) ;
if ( temp & MSR_RDF )
lpi2c_imx_read_rxfifo ( lpi2c_imx ) ;
if ( temp & MSR_TDF )
lpi2c_imx_write_txfifo ( lpi2c_imx ) ;
if ( temp & MSR_NDF )
complete ( & lpi2c_imx - > complete ) ;
return IRQ_HANDLED ;
}
static u32 lpi2c_imx_func ( struct i2c_adapter * adapter )
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_READ_BLOCK_DATA ;
}
2017-01-27 21:06:17 +03:00
static const struct i2c_algorithm lpi2c_imx_algo = {
2016-11-30 05:40:47 +03:00
. master_xfer = lpi2c_imx_xfer ,
. functionality = lpi2c_imx_func ,
} ;
static const struct of_device_id lpi2c_imx_of_match [ ] = {
{ . compatible = " fsl,imx7ulp-lpi2c " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , lpi2c_imx_of_match ) ;
static int lpi2c_imx_probe ( struct platform_device * pdev )
{
struct lpi2c_imx_struct * lpi2c_imx ;
struct resource * res ;
2016-12-02 06:38:16 +03:00
unsigned int temp ;
2016-11-30 05:40:47 +03:00
int irq , ret ;
lpi2c_imx = devm_kzalloc ( & pdev - > dev , sizeof ( * lpi2c_imx ) , GFP_KERNEL ) ;
if ( ! lpi2c_imx )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
lpi2c_imx - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( lpi2c_imx - > base ) )
return PTR_ERR ( lpi2c_imx - > base ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
dev_err ( & pdev - > dev , " can't get irq number \n " ) ;
return irq ;
}
lpi2c_imx - > adapter . owner = THIS_MODULE ;
lpi2c_imx - > adapter . algo = & lpi2c_imx_algo ;
lpi2c_imx - > adapter . dev . parent = & pdev - > dev ;
lpi2c_imx - > adapter . dev . of_node = pdev - > dev . of_node ;
strlcpy ( lpi2c_imx - > adapter . name , pdev - > name ,
sizeof ( lpi2c_imx - > adapter . name ) ) ;
lpi2c_imx - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( lpi2c_imx - > clk ) ) {
dev_err ( & pdev - > dev , " can't get I2C peripheral clock \n " ) ;
return PTR_ERR ( lpi2c_imx - > clk ) ;
}
ret = of_property_read_u32 ( pdev - > dev . of_node ,
" clock-frequency " , & lpi2c_imx - > bitrate ) ;
if ( ret )
lpi2c_imx - > bitrate = LPI2C_DEFAULT_RATE ;
ret = devm_request_irq ( & pdev - > dev , irq , lpi2c_imx_isr , 0 ,
pdev - > name , lpi2c_imx ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " can't claim irq %d \n " , irq ) ;
return ret ;
}
i2c_set_adapdata ( & lpi2c_imx - > adapter , lpi2c_imx ) ;
platform_set_drvdata ( pdev , lpi2c_imx ) ;
2016-12-02 06:38:16 +03:00
ret = clk_prepare_enable ( lpi2c_imx - > clk ) ;
2016-11-30 05:40:47 +03:00
if ( ret ) {
2016-12-02 06:38:16 +03:00
dev_err ( & pdev - > dev , " clk enable failed %d \n " , ret ) ;
2016-11-30 05:40:47 +03:00
return ret ;
}
2018-01-02 12:11:52 +03:00
pm_runtime_set_autosuspend_delay ( & pdev - > dev , I2C_PM_TIMEOUT ) ;
pm_runtime_use_autosuspend ( & pdev - > dev ) ;
pm_runtime_get_noresume ( & pdev - > dev ) ;
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2016-12-02 06:38:16 +03:00
temp = readl ( lpi2c_imx - > base + LPI2C_PARAM ) ;
lpi2c_imx - > txfifosize = 1 < < ( temp & 0x0f ) ;
lpi2c_imx - > rxfifosize = 1 < < ( ( temp > > 8 ) & 0x0f ) ;
2016-11-30 05:40:47 +03:00
ret = i2c_add_adapter ( & lpi2c_imx - > adapter ) ;
if ( ret )
2018-01-02 12:11:52 +03:00
goto rpm_disable ;
pm_runtime_mark_last_busy ( & pdev - > dev ) ;
pm_runtime_put_autosuspend ( & pdev - > dev ) ;
2016-11-30 05:40:47 +03:00
dev_info ( & lpi2c_imx - > adapter . dev , " LPI2C adapter registered \n " ) ;
return 0 ;
2018-01-02 12:11:52 +03:00
rpm_disable :
pm_runtime_put ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_dont_use_autosuspend ( & pdev - > dev ) ;
2016-11-30 05:40:47 +03:00
return ret ;
}
static int lpi2c_imx_remove ( struct platform_device * pdev )
{
struct lpi2c_imx_struct * lpi2c_imx = platform_get_drvdata ( pdev ) ;
i2c_del_adapter ( & lpi2c_imx - > adapter ) ;
2018-01-02 12:11:52 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_dont_use_autosuspend ( & pdev - > dev ) ;
2016-11-30 05:40:47 +03:00
return 0 ;
}
2017-01-17 13:20:55 +03:00
# ifdef CONFIG_PM_SLEEP
2018-01-02 12:11:52 +03:00
static int lpi2c_runtime_suspend ( struct device * dev )
2017-01-17 13:20:55 +03:00
{
2018-01-02 12:11:52 +03:00
struct lpi2c_imx_struct * lpi2c_imx = dev_get_drvdata ( dev ) ;
clk_disable_unprepare ( lpi2c_imx - > clk ) ;
2017-01-17 13:20:55 +03:00
pinctrl_pm_select_sleep_state ( dev ) ;
return 0 ;
}
2018-01-02 12:11:52 +03:00
static int lpi2c_runtime_resume ( struct device * dev )
2017-01-17 13:20:55 +03:00
{
2018-01-02 12:11:52 +03:00
struct lpi2c_imx_struct * lpi2c_imx = dev_get_drvdata ( dev ) ;
int ret ;
2017-01-17 13:20:55 +03:00
pinctrl_pm_select_default_state ( dev ) ;
2018-01-02 12:11:52 +03:00
ret = clk_prepare_enable ( lpi2c_imx - > clk ) ;
if ( ret ) {
dev_err ( dev , " failed to enable I2C clock, ret=%d \n " , ret ) ;
return ret ;
}
2017-01-17 13:20:55 +03:00
return 0 ;
}
2018-01-02 12:11:52 +03:00
static const struct dev_pm_ops lpi2c_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend ,
pm_runtime_force_resume )
SET_RUNTIME_PM_OPS ( lpi2c_runtime_suspend ,
lpi2c_runtime_resume , NULL )
} ;
# define IMX_LPI2C_PM (&lpi2c_pm_ops)
# else
# define IMX_LPI2C_PM NULL
# endif
2017-01-17 13:20:55 +03:00
2016-11-30 05:40:47 +03:00
static struct platform_driver lpi2c_imx_driver = {
. probe = lpi2c_imx_probe ,
. remove = lpi2c_imx_remove ,
. driver = {
. name = DRIVER_NAME ,
. of_match_table = lpi2c_imx_of_match ,
2018-01-02 12:11:52 +03:00
. pm = IMX_LPI2C_PM ,
2016-11-30 05:40:47 +03:00
} ,
} ;
module_platform_driver ( lpi2c_imx_driver ) ;
MODULE_AUTHOR ( " Gao Pan <pandy.gao@nxp.com> " ) ;
MODULE_DESCRIPTION ( " I2C adapter driver for LPI2C bus " ) ;
MODULE_LICENSE ( " GPL " ) ;