forked from mirrors/linux
		
	i2c: designware: add i2c gpio recovery option
This patch contains much input from Phil Reid and has been tested on Intel/Altera Cyclone V SOC Hardware with Altera GPIO's for the SCL and SDA GPIO's. Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Tim Sander <tim@krieglstein.org> Signed-off-by: Phil Reid <preid@electromag.com.au> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
		
							parent
							
								
									0326f9f801
								
							
						
					
					
						commit
						ca382f5b38
					
				
					 3 changed files with 63 additions and 1 deletions
				
			
		|  | @ -230,7 +230,11 @@ int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) | |||
| 	while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) { | ||||
| 		if (timeout <= 0) { | ||||
| 			dev_warn(dev->dev, "timeout waiting for bus ready\n"); | ||||
| 			return -ETIMEDOUT; | ||||
| 			i2c_recover_bus(&dev->adapter); | ||||
| 
 | ||||
| 			if (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) | ||||
| 				return -ETIMEDOUT; | ||||
| 			return 0; | ||||
| 		} | ||||
| 		timeout--; | ||||
| 		usleep_range(1000, 1100); | ||||
|  |  | |||
|  | @ -286,6 +286,7 @@ struct dw_i2c_dev { | |||
| 	void			(*disable_int)(struct dw_i2c_dev *dev); | ||||
| 	int			(*init)(struct dw_i2c_dev *dev); | ||||
| 	int			mode; | ||||
| 	struct i2c_bus_recovery_info rinfo; | ||||
| }; | ||||
| 
 | ||||
| #define ACCESS_SWAP		0x00000001 | ||||
|  |  | |||
|  | @ -25,11 +25,13 @@ | |||
| #include <linux/err.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/export.h> | ||||
| #include <linux/gpio/consumer.h> | ||||
| #include <linux/i2c.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/pm_runtime.h> | ||||
| #include <linux/reset.h> | ||||
| 
 | ||||
| #include "i2c-designware-core.h" | ||||
| 
 | ||||
|  | @ -443,6 +445,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) | |||
| 	if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) { | ||||
| 		dev_err(dev->dev, "controller timed out\n"); | ||||
| 		/* i2c_dw_init implicitly disables the adapter */ | ||||
| 		i2c_recover_bus(&dev->adapter); | ||||
| 		i2c_dw_init_master(dev); | ||||
| 		ret = -ETIMEDOUT; | ||||
| 		goto done; | ||||
|  | @ -613,6 +616,56 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) | |||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static void i2c_dw_prepare_recovery(struct i2c_adapter *adap) | ||||
| { | ||||
| 	struct dw_i2c_dev *dev = i2c_get_adapdata(adap); | ||||
| 
 | ||||
| 	i2c_dw_disable(dev); | ||||
| 	reset_control_assert(dev->rst); | ||||
| 	i2c_dw_prepare_clk(dev, false); | ||||
| } | ||||
| 
 | ||||
| static void i2c_dw_unprepare_recovery(struct i2c_adapter *adap) | ||||
| { | ||||
| 	struct dw_i2c_dev *dev = i2c_get_adapdata(adap); | ||||
| 
 | ||||
| 	i2c_dw_prepare_clk(dev, true); | ||||
| 	reset_control_deassert(dev->rst); | ||||
| 	i2c_dw_init_master(dev); | ||||
| } | ||||
| 
 | ||||
| static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev) | ||||
| { | ||||
| 	struct i2c_bus_recovery_info *rinfo = &dev->rinfo; | ||||
| 	struct i2c_adapter *adap = &dev->adapter; | ||||
| 	struct gpio_desc *gpio; | ||||
| 	int r; | ||||
| 
 | ||||
| 	gpio = devm_gpiod_get(dev->dev, "scl", GPIOD_OUT_HIGH); | ||||
| 	if (IS_ERR(gpio)) { | ||||
| 		r = PTR_ERR(gpio); | ||||
| 		if (r == -ENOENT) | ||||
| 			return 0; | ||||
| 		return r; | ||||
| 	} | ||||
| 	rinfo->scl_gpiod = gpio; | ||||
| 
 | ||||
| 	gpio = devm_gpiod_get_optional(dev->dev, "sda", GPIOD_IN); | ||||
| 	if (IS_ERR(gpio)) | ||||
| 		return PTR_ERR(gpio); | ||||
| 	rinfo->sda_gpiod = gpio; | ||||
| 
 | ||||
| 	rinfo->recover_bus = i2c_generic_scl_recovery; | ||||
| 	rinfo->prepare_recovery = i2c_dw_prepare_recovery; | ||||
| 	rinfo->unprepare_recovery = i2c_dw_unprepare_recovery; | ||||
| 	adap->bus_recovery_info = rinfo; | ||||
| 
 | ||||
| 	dev_info(dev->dev, "running with gpio recovery mode! scl%s", | ||||
| 		 rinfo->sda_gpiod ? ",sda" : ""); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int i2c_dw_probe(struct dw_i2c_dev *dev) | ||||
| { | ||||
| 	struct i2c_adapter *adap = &dev->adapter; | ||||
|  | @ -652,6 +705,10 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) | |||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = i2c_dw_init_recovery_info(dev); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Increment PM usage count during adapter registration in order to | ||||
| 	 * avoid possible spurious runtime suspend when adapter device is | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Tim Sander
						Tim Sander