mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	i2c: removed work arounds in i2c driver for Zynq Ultrascale+ MPSoC
Cadence 1.0 version has bugs which have been fixed in the cadence 1.4 version. This patch removes the quirks present in the driver for cadence 1.4 version. Signed-off-by: Anurag Kumar Vulisha <anuragku@xilinx.com> [wsa: fixed indentation issues in r1p10_i2c_def] Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
		
							parent
							
								
									d57f5dedde
								
							
						
					
					
						commit
						63cab195bf
					
				
					 2 changed files with 62 additions and 12 deletions
				
			
		| 
						 | 
					@ -2,7 +2,11 @@ Binding for the Cadence I2C controller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Required properties:
 | 
					Required properties:
 | 
				
			||||||
  - reg: Physical base address and size of the controller's register area.
 | 
					  - reg: Physical base address and size of the controller's register area.
 | 
				
			||||||
  - compatible: Compatibility string. Must be 'cdns,i2c-r1p10'.
 | 
					  - compatible: Should contain one of:
 | 
				
			||||||
 | 
							* "cdns,i2c-r1p10"
 | 
				
			||||||
 | 
							Note:	Use this when cadence i2c controller version 1.0 is used.
 | 
				
			||||||
 | 
							* "cdns,i2c-r1p14"
 | 
				
			||||||
 | 
							Note:	Use this when cadence i2c controller version 1.4 is used.
 | 
				
			||||||
  - clocks: Input clock specifier. Refer to common clock bindings.
 | 
					  - clocks: Input clock specifier. Refer to common clock bindings.
 | 
				
			||||||
  - interrupts: Interrupt specifier. Refer to interrupt bindings.
 | 
					  - interrupts: Interrupt specifier. Refer to interrupt bindings.
 | 
				
			||||||
  - #address-cells: Should be 1.
 | 
					  - #address-cells: Should be 1.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@
 | 
				
			||||||
#include <linux/io.h>
 | 
					#include <linux/io.h>
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/platform_device.h>
 | 
					#include <linux/platform_device.h>
 | 
				
			||||||
 | 
					#include <linux/of.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Register offsets for the I2C device. */
 | 
					/* Register offsets for the I2C device. */
 | 
				
			||||||
#define CDNS_I2C_CR_OFFSET		0x00 /* Control Register, RW */
 | 
					#define CDNS_I2C_CR_OFFSET		0x00 /* Control Register, RW */
 | 
				
			||||||
| 
						 | 
					@ -113,6 +114,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define CDNS_I2C_TIMEOUT_MAX	0xFF
 | 
					#define CDNS_I2C_TIMEOUT_MAX	0xFF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CDNS_I2C_BROKEN_HOLD_BIT	BIT(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define cdns_i2c_readreg(offset)       readl_relaxed(id->membase + offset)
 | 
					#define cdns_i2c_readreg(offset)       readl_relaxed(id->membase + offset)
 | 
				
			||||||
#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
 | 
					#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -135,6 +138,7 @@
 | 
				
			||||||
 * @bus_hold_flag:	Flag used in repeated start for clearing HOLD bit
 | 
					 * @bus_hold_flag:	Flag used in repeated start for clearing HOLD bit
 | 
				
			||||||
 * @clk:		Pointer to struct clk
 | 
					 * @clk:		Pointer to struct clk
 | 
				
			||||||
 * @clk_rate_change_nb:	Notifier block for clock rate changes
 | 
					 * @clk_rate_change_nb:	Notifier block for clock rate changes
 | 
				
			||||||
 | 
					 * @quirks:		flag for broken hold bit usage in r1p10
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct cdns_i2c {
 | 
					struct cdns_i2c {
 | 
				
			||||||
	void __iomem *membase;
 | 
						void __iomem *membase;
 | 
				
			||||||
| 
						 | 
					@ -154,6 +158,11 @@ struct cdns_i2c {
 | 
				
			||||||
	unsigned int bus_hold_flag;
 | 
						unsigned int bus_hold_flag;
 | 
				
			||||||
	struct clk *clk;
 | 
						struct clk *clk;
 | 
				
			||||||
	struct notifier_block clk_rate_change_nb;
 | 
						struct notifier_block clk_rate_change_nb;
 | 
				
			||||||
 | 
						u32 quirks;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct cdns_platform_data {
 | 
				
			||||||
 | 
						u32 quirks;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define to_cdns_i2c(_nb)	container_of(_nb, struct cdns_i2c, \
 | 
					#define to_cdns_i2c(_nb)	container_of(_nb, struct cdns_i2c, \
 | 
				
			||||||
| 
						 | 
					@ -172,6 +181,12 @@ static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id)
 | 
				
			||||||
		cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET);
 | 
							cdns_i2c_writereg(reg & ~CDNS_I2C_CR_HOLD, CDNS_I2C_CR_OFFSET);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool cdns_is_holdquirk(struct cdns_i2c *id, bool hold_wrkaround)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (hold_wrkaround &&
 | 
				
			||||||
 | 
							(id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * cdns_i2c_isr - Interrupt handler for the I2C device
 | 
					 * cdns_i2c_isr - Interrupt handler for the I2C device
 | 
				
			||||||
 * @irq:	irq number for the I2C device
 | 
					 * @irq:	irq number for the I2C device
 | 
				
			||||||
| 
						 | 
					@ -186,6 +201,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned int isr_status, avail_bytes, updatetx;
 | 
						unsigned int isr_status, avail_bytes, updatetx;
 | 
				
			||||||
	unsigned int bytes_to_send;
 | 
						unsigned int bytes_to_send;
 | 
				
			||||||
 | 
						bool hold_quirk;
 | 
				
			||||||
	struct cdns_i2c *id = ptr;
 | 
						struct cdns_i2c *id = ptr;
 | 
				
			||||||
	/* Signal completion only after everything is updated */
 | 
						/* Signal completion only after everything is updated */
 | 
				
			||||||
	int done_flag = 0;
 | 
						int done_flag = 0;
 | 
				
			||||||
| 
						 | 
					@ -208,6 +224,8 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
 | 
				
			||||||
	if (id->recv_count > id->curr_recv_count)
 | 
						if (id->recv_count > id->curr_recv_count)
 | 
				
			||||||
		updatetx = 1;
 | 
							updatetx = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hold_quirk = (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* When receiving, handle data interrupt and completion interrupt */
 | 
						/* When receiving, handle data interrupt and completion interrupt */
 | 
				
			||||||
	if (id->p_recv_buf &&
 | 
						if (id->p_recv_buf &&
 | 
				
			||||||
	    ((isr_status & CDNS_I2C_IXR_COMP) ||
 | 
						    ((isr_status & CDNS_I2C_IXR_COMP) ||
 | 
				
			||||||
| 
						 | 
					@ -229,8 +247,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
 | 
				
			||||||
			id->recv_count--;
 | 
								id->recv_count--;
 | 
				
			||||||
			id->curr_recv_count--;
 | 
								id->curr_recv_count--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (updatetx &&
 | 
								if (cdns_is_holdquirk(id, hold_quirk))
 | 
				
			||||||
			    (id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1))
 | 
					 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -241,8 +258,7 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
 | 
				
			||||||
		 * maintain transfer size non-zero while performing a large
 | 
							 * maintain transfer size non-zero while performing a large
 | 
				
			||||||
		 * receive operation.
 | 
							 * receive operation.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		if (updatetx &&
 | 
							if (cdns_is_holdquirk(id, hold_quirk)) {
 | 
				
			||||||
		    (id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1)) {
 | 
					 | 
				
			||||||
			/* wait while fifo is full */
 | 
								/* wait while fifo is full */
 | 
				
			||||||
			while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=
 | 
								while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=
 | 
				
			||||||
			       (id->curr_recv_count - CDNS_I2C_FIFO_DEPTH))
 | 
								       (id->curr_recv_count - CDNS_I2C_FIFO_DEPTH))
 | 
				
			||||||
| 
						 | 
					@ -264,6 +280,22 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
 | 
				
			||||||
						  CDNS_I2C_XFER_SIZE_OFFSET);
 | 
											  CDNS_I2C_XFER_SIZE_OFFSET);
 | 
				
			||||||
				id->curr_recv_count = id->recv_count;
 | 
									id->curr_recv_count = id->recv_count;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							} else if (id->recv_count && !hold_quirk &&
 | 
				
			||||||
 | 
											!id->curr_recv_count) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Set the slave address in address register*/
 | 
				
			||||||
 | 
								cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
 | 
				
			||||||
 | 
											CDNS_I2C_ADDR_OFFSET);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) {
 | 
				
			||||||
 | 
									cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
 | 
				
			||||||
 | 
											CDNS_I2C_XFER_SIZE_OFFSET);
 | 
				
			||||||
 | 
									id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									cdns_i2c_writereg(id->recv_count,
 | 
				
			||||||
 | 
											CDNS_I2C_XFER_SIZE_OFFSET);
 | 
				
			||||||
 | 
									id->curr_recv_count = id->recv_count;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Clear hold (if not repeated start) and signal completion */
 | 
							/* Clear hold (if not repeated start) and signal completion */
 | 
				
			||||||
| 
						 | 
					@ -535,11 +567,13 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
 | 
				
			||||||
	int ret, count;
 | 
						int ret, count;
 | 
				
			||||||
	u32 reg;
 | 
						u32 reg;
 | 
				
			||||||
	struct cdns_i2c *id = adap->algo_data;
 | 
						struct cdns_i2c *id = adap->algo_data;
 | 
				
			||||||
 | 
						bool hold_quirk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Check if the bus is free */
 | 
						/* Check if the bus is free */
 | 
				
			||||||
	if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
 | 
						if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
 | 
				
			||||||
		return -EAGAIN;
 | 
							return -EAGAIN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Set the flag to one when multiple messages are to be
 | 
						 * Set the flag to one when multiple messages are to be
 | 
				
			||||||
	 * processed with a repeated start.
 | 
						 * processed with a repeated start.
 | 
				
			||||||
| 
						 | 
					@ -552,7 +586,7 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
 | 
				
			||||||
		 * followed by any other message, an error is returned
 | 
							 * followed by any other message, an error is returned
 | 
				
			||||||
		 * indicating that this sequence is not supported.
 | 
							 * indicating that this sequence is not supported.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		for (count = 0; count < num - 1; count++) {
 | 
							for (count = 0; (count < num - 1 && hold_quirk); count++) {
 | 
				
			||||||
			if (msgs[count].flags & I2C_M_RD) {
 | 
								if (msgs[count].flags & I2C_M_RD) {
 | 
				
			||||||
				dev_warn(adap->dev.parent,
 | 
									dev_warn(adap->dev.parent,
 | 
				
			||||||
					 "Can't do repeated start after a receive message\n");
 | 
										 "Can't do repeated start after a receive message\n");
 | 
				
			||||||
| 
						 | 
					@ -815,6 +849,17 @@ static int __maybe_unused cdns_i2c_resume(struct device *_dev)
 | 
				
			||||||
static SIMPLE_DEV_PM_OPS(cdns_i2c_dev_pm_ops, cdns_i2c_suspend,
 | 
					static SIMPLE_DEV_PM_OPS(cdns_i2c_dev_pm_ops, cdns_i2c_suspend,
 | 
				
			||||||
			 cdns_i2c_resume);
 | 
								 cdns_i2c_resume);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct cdns_platform_data r1p10_i2c_def = {
 | 
				
			||||||
 | 
						.quirks = CDNS_I2C_BROKEN_HOLD_BIT,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct of_device_id cdns_i2c_of_match[] = {
 | 
				
			||||||
 | 
						{ .compatible = "cdns,i2c-r1p10", .data = &r1p10_i2c_def },
 | 
				
			||||||
 | 
						{ .compatible = "cdns,i2c-r1p14",},
 | 
				
			||||||
 | 
						{ /* end of table */ }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * cdns_i2c_probe - Platform registration call
 | 
					 * cdns_i2c_probe - Platform registration call
 | 
				
			||||||
 * @pdev:	Handle to the platform device structure
 | 
					 * @pdev:	Handle to the platform device structure
 | 
				
			||||||
| 
						 | 
					@ -830,6 +875,7 @@ static int cdns_i2c_probe(struct platform_device *pdev)
 | 
				
			||||||
	struct resource *r_mem;
 | 
						struct resource *r_mem;
 | 
				
			||||||
	struct cdns_i2c *id;
 | 
						struct cdns_i2c *id;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
						const struct of_device_id *match;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL);
 | 
						id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL);
 | 
				
			||||||
	if (!id)
 | 
						if (!id)
 | 
				
			||||||
| 
						 | 
					@ -837,6 +883,12 @@ static int cdns_i2c_probe(struct platform_device *pdev)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	platform_set_drvdata(pdev, id);
 | 
						platform_set_drvdata(pdev, id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						match = of_match_node(cdns_i2c_of_match, pdev->dev.of_node);
 | 
				
			||||||
 | 
						if (match && match->data) {
 | 
				
			||||||
 | 
							const struct cdns_platform_data *data = match->data;
 | 
				
			||||||
 | 
							id->quirks = data->quirks;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
						r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
				
			||||||
	id->membase = devm_ioremap_resource(&pdev->dev, r_mem);
 | 
						id->membase = devm_ioremap_resource(&pdev->dev, r_mem);
 | 
				
			||||||
	if (IS_ERR(id->membase))
 | 
						if (IS_ERR(id->membase))
 | 
				
			||||||
| 
						 | 
					@ -935,12 +987,6 @@ static int cdns_i2c_remove(struct platform_device *pdev)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct of_device_id cdns_i2c_of_match[] = {
 | 
					 | 
				
			||||||
	{ .compatible = "cdns,i2c-r1p10", },
 | 
					 | 
				
			||||||
	{ /* end of table */ }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct platform_driver cdns_i2c_drv = {
 | 
					static struct platform_driver cdns_i2c_drv = {
 | 
				
			||||||
	.driver = {
 | 
						.driver = {
 | 
				
			||||||
		.name  = DRIVER_NAME,
 | 
							.name  = DRIVER_NAME,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue