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) {
 | 
						while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
 | 
				
			||||||
		if (timeout <= 0) {
 | 
							if (timeout <= 0) {
 | 
				
			||||||
			dev_warn(dev->dev, "timeout waiting for bus ready\n");
 | 
								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--;
 | 
							timeout--;
 | 
				
			||||||
		usleep_range(1000, 1100);
 | 
							usleep_range(1000, 1100);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -286,6 +286,7 @@ struct dw_i2c_dev {
 | 
				
			||||||
	void			(*disable_int)(struct dw_i2c_dev *dev);
 | 
						void			(*disable_int)(struct dw_i2c_dev *dev);
 | 
				
			||||||
	int			(*init)(struct dw_i2c_dev *dev);
 | 
						int			(*init)(struct dw_i2c_dev *dev);
 | 
				
			||||||
	int			mode;
 | 
						int			mode;
 | 
				
			||||||
 | 
						struct i2c_bus_recovery_info rinfo;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ACCESS_SWAP		0x00000001
 | 
					#define ACCESS_SWAP		0x00000001
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,11 +25,13 @@
 | 
				
			||||||
#include <linux/err.h>
 | 
					#include <linux/err.h>
 | 
				
			||||||
#include <linux/errno.h>
 | 
					#include <linux/errno.h>
 | 
				
			||||||
#include <linux/export.h>
 | 
					#include <linux/export.h>
 | 
				
			||||||
 | 
					#include <linux/gpio/consumer.h>
 | 
				
			||||||
#include <linux/i2c.h>
 | 
					#include <linux/i2c.h>
 | 
				
			||||||
#include <linux/interrupt.h>
 | 
					#include <linux/interrupt.h>
 | 
				
			||||||
#include <linux/io.h>
 | 
					#include <linux/io.h>
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/pm_runtime.h>
 | 
					#include <linux/pm_runtime.h>
 | 
				
			||||||
 | 
					#include <linux/reset.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "i2c-designware-core.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)) {
 | 
						if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) {
 | 
				
			||||||
		dev_err(dev->dev, "controller timed out\n");
 | 
							dev_err(dev->dev, "controller timed out\n");
 | 
				
			||||||
		/* i2c_dw_init implicitly disables the adapter */
 | 
							/* i2c_dw_init implicitly disables the adapter */
 | 
				
			||||||
 | 
							i2c_recover_bus(&dev->adapter);
 | 
				
			||||||
		i2c_dw_init_master(dev);
 | 
							i2c_dw_init_master(dev);
 | 
				
			||||||
		ret = -ETIMEDOUT;
 | 
							ret = -ETIMEDOUT;
 | 
				
			||||||
		goto done;
 | 
							goto done;
 | 
				
			||||||
| 
						 | 
					@ -613,6 +616,56 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
 | 
				
			||||||
	return IRQ_HANDLED;
 | 
						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)
 | 
					int i2c_dw_probe(struct dw_i2c_dev *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct i2c_adapter *adap = &dev->adapter;
 | 
						struct i2c_adapter *adap = &dev->adapter;
 | 
				
			||||||
| 
						 | 
					@ -652,6 +705,10 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = i2c_dw_init_recovery_info(dev);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Increment PM usage count during adapter registration in order to
 | 
						 * Increment PM usage count during adapter registration in order to
 | 
				
			||||||
	 * avoid possible spurious runtime suspend when adapter device is
 | 
						 * avoid possible spurious runtime suspend when adapter device is
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue