diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-pnx.c')
| -rw-r--r-- | drivers/i2c/busses/i2c-pnx.c | 271 | 
1 files changed, 158 insertions, 113 deletions
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index a97e3fec814..dc7ff829ad7 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -23,16 +23,62 @@  #include <linux/err.h>  #include <linux/clk.h>  #include <linux/slab.h> +#include <linux/of.h> + +#define I2C_PNX_TIMEOUT_DEFAULT		10 /* msec */ +#define I2C_PNX_SPEED_KHZ_DEFAULT	100 +#define I2C_PNX_REGION_SIZE		0x100 + +enum { +	mstatus_tdi = 0x00000001, +	mstatus_afi = 0x00000002, +	mstatus_nai = 0x00000004, +	mstatus_drmi = 0x00000008, +	mstatus_active = 0x00000020, +	mstatus_scl = 0x00000040, +	mstatus_sda = 0x00000080, +	mstatus_rff = 0x00000100, +	mstatus_rfe = 0x00000200, +	mstatus_tff = 0x00000400, +	mstatus_tfe = 0x00000800, +}; -#include <mach/hardware.h> -#include <mach/i2c.h> +enum { +	mcntrl_tdie = 0x00000001, +	mcntrl_afie = 0x00000002, +	mcntrl_naie = 0x00000004, +	mcntrl_drmie = 0x00000008, +	mcntrl_drsie = 0x00000010, +	mcntrl_rffie = 0x00000020, +	mcntrl_daie = 0x00000040, +	mcntrl_tffie = 0x00000080, +	mcntrl_reset = 0x00000100, +	mcntrl_cdbmode = 0x00000400, +}; -#define I2C_PNX_TIMEOUT		10 /* msec */ -#define I2C_PNX_SPEED_KHZ	100 -#define I2C_PNX_REGION_SIZE	0x100 +enum { +	rw_bit = 1 << 0, +	start_bit = 1 << 8, +	stop_bit = 1 << 9, +}; -static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data) +#define I2C_REG_RX(a)	((a)->ioaddr)		/* Rx FIFO reg (RO) */ +#define I2C_REG_TX(a)	((a)->ioaddr)		/* Tx FIFO reg (WO) */ +#define I2C_REG_STS(a)	((a)->ioaddr + 0x04)	/* Status reg (RO) */ +#define I2C_REG_CTL(a)	((a)->ioaddr + 0x08)	/* Ctl reg */ +#define I2C_REG_CKL(a)	((a)->ioaddr + 0x0c)	/* Clock divider low */ +#define I2C_REG_CKH(a)	((a)->ioaddr + 0x10)	/* Clock divider high */ +#define I2C_REG_ADR(a)	((a)->ioaddr + 0x14)	/* I2C address */ +#define I2C_REG_RFL(a)	((a)->ioaddr + 0x18)	/* Rx FIFO level (RO) */ +#define I2C_REG_TFL(a)	((a)->ioaddr + 0x1c)	/* Tx FIFO level (RO) */ +#define I2C_REG_RXB(a)	((a)->ioaddr + 0x20)	/* Num of bytes Rx-ed (RO) */ +#define I2C_REG_TXB(a)	((a)->ioaddr + 0x24)	/* Num of bytes Tx-ed (RO) */ +#define I2C_REG_TXS(a)	((a)->ioaddr + 0x28)	/* Tx slave FIFO (RO) */ +#define I2C_REG_STFL(a)	((a)->ioaddr + 0x2c)	/* Tx slave FIFO level (RO) */ + +static inline int wait_timeout(struct i2c_pnx_algo_data *data)  { +	long timeout = data->timeout;  	while (timeout > 0 &&  			(ioread32(I2C_REG_STS(data)) & mstatus_active)) {  		mdelay(1); @@ -41,8 +87,9 @@ static inline int wait_timeout(long timeout, struct i2c_pnx_algo_data *data)  	return (timeout <= 0);  } -static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data) +static inline int wait_reset(struct i2c_pnx_algo_data *data)  { +	long timeout = data->timeout;  	while (timeout > 0 &&  			(ioread32(I2C_REG_CTL(data)) & mcntrl_reset)) {  		mdelay(1); @@ -54,7 +101,7 @@ static inline int wait_reset(long timeout, struct i2c_pnx_algo_data *data)  static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data)  {  	struct timer_list *timer = &alg_data->mif.timer; -	unsigned long expires = msecs_to_jiffies(I2C_PNX_TIMEOUT); +	unsigned long expires = msecs_to_jiffies(alg_data->timeout);  	if (expires <= 1)  		expires = 2; @@ -65,7 +112,7 @@ static inline void i2c_pnx_arm_timer(struct i2c_pnx_algo_data *alg_data)  		jiffies, expires);  	timer->expires = jiffies + expires; -	timer->data = (unsigned long)&alg_data; +	timer->data = (unsigned long)alg_data;  	add_timer(timer);  } @@ -92,7 +139,7 @@ static int i2c_pnx_start(unsigned char slave_addr,  	}  	/* First, make sure bus is idle */ -	if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) { +	if (wait_timeout(alg_data)) {  		/* Somebody else is monopolizing the bus */  		dev_err(&alg_data->adapter.dev,  			"%s: Bus busy. Slave addr = %02x, cntrl = %x, stat = %x\n", @@ -185,7 +232,7 @@ static int i2c_pnx_master_xmit(struct i2c_pnx_algo_data *alg_data)  		if (alg_data->mif.len == 0) {  			if (alg_data->last) {  				/* Wait until the STOP is seen. */ -				if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) +				if (wait_timeout(alg_data))  					dev_err(&alg_data->adapter.dev,  						"The bus is still active after timeout\n");  			} @@ -244,31 +291,37 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)  	 * or we didn't 'ask' for it yet.  	 */  	if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) { -		dev_dbg(&alg_data->adapter.dev, -			"%s(): Write dummy data to fill Rx-fifo...\n", -			__func__); +		/* 'Asking' is done asynchronously, e.g. dummy TX of several +		 * bytes is done before the first actual RX arrives in FIFO. +		 * Therefore, ordered bytes (via TX) are counted separately. +		 */ +		if (alg_data->mif.order) { +			dev_dbg(&alg_data->adapter.dev, +				"%s(): Write dummy data to fill Rx-fifo...\n", +				__func__); -		if (alg_data->mif.len == 1) { -			/* Last byte, do not acknowledge next rcv. */ -			val |= stop_bit; +			if (alg_data->mif.order == 1) { +				/* Last byte, do not acknowledge next rcv. */ +				val |= stop_bit; + +				/* +				 * Enable interrupt RFDAIE (data in Rx fifo), +				 * and disable DRMIE (need data for Tx) +				 */ +				ctl = ioread32(I2C_REG_CTL(alg_data)); +				ctl |= mcntrl_rffie | mcntrl_daie; +				ctl &= ~mcntrl_drmie; +				iowrite32(ctl, I2C_REG_CTL(alg_data)); +			}  			/* -			 * Enable interrupt RFDAIE (data in Rx fifo), -			 * and disable DRMIE (need data for Tx) +			 * Now we'll 'ask' for data: +			 * For each byte we want to receive, we must +			 * write a (dummy) byte to the Tx-FIFO.  			 */ -			ctl = ioread32(I2C_REG_CTL(alg_data)); -			ctl |= mcntrl_rffie | mcntrl_daie; -			ctl &= ~mcntrl_drmie; -			iowrite32(ctl, I2C_REG_CTL(alg_data)); +			iowrite32(val, I2C_REG_TX(alg_data)); +			alg_data->mif.order--;  		} - -		/* -		 * Now we'll 'ask' for data: -		 * For each byte we want to receive, we must -		 * write a (dummy) byte to the Tx-FIFO. -		 */ -		iowrite32(val, I2C_REG_TX(alg_data)); -  		return 0;  	} @@ -283,7 +336,7 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)  		if (alg_data->mif.len == 0) {  			if (alg_data->last)  				/* Wait until the STOP is seen. */ -				if (wait_timeout(I2C_PNX_TIMEOUT, alg_data)) +				if (wait_timeout(alg_data))  					dev_err(&alg_data->adapter.dev,  						"The bus is still active after timeout\n"); @@ -399,7 +452,7 @@ static void i2c_pnx_timeout(unsigned long data)  	ctl |= mcntrl_reset;  	iowrite32(ctl, I2C_REG_CTL(alg_data)); -	wait_reset(I2C_PNX_TIMEOUT, alg_data); +	wait_reset(alg_data);  	alg_data->mif.ret = -EIO;  	complete(&alg_data->mif.complete);  } @@ -414,18 +467,18 @@ static inline void bus_reset_if_active(struct i2c_pnx_algo_data *alg_data)  			alg_data->adapter.name);  		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,  			  I2C_REG_CTL(alg_data)); -		wait_reset(I2C_PNX_TIMEOUT, alg_data); +		wait_reset(alg_data);  	} else if (!(stat & mstatus_rfe) || !(stat & mstatus_tfe)) {  		/* If there is data in the fifo's after transfer,  		 * flush fifo's by reset.  		 */  		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,  			  I2C_REG_CTL(alg_data)); -		wait_reset(I2C_PNX_TIMEOUT, alg_data); +		wait_reset(alg_data);  	} else if (stat & mstatus_nai) {  		iowrite32(ioread32(I2C_REG_CTL(alg_data)) | mcntrl_reset,  			  I2C_REG_CTL(alg_data)); -		wait_reset(I2C_PNX_TIMEOUT, alg_data); +		wait_reset(alg_data);  	}  } @@ -468,6 +521,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  		alg_data->mif.buf = pmsg->buf;  		alg_data->mif.len = pmsg->len; +		alg_data->mif.order = pmsg->len;  		alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ?  			I2C_SMBUS_READ : I2C_SMBUS_WRITE;  		alg_data->mif.ret = 0; @@ -520,6 +574,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  	/* Cleanup to be sure... */  	alg_data->mif.buf = NULL;  	alg_data->mif.len = 0; +	alg_data->mif.order = 0;  	dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n",  		__func__, ioread32(I2C_REG_STS(alg_data))); @@ -540,90 +595,86 @@ static struct i2c_algorithm pnx_algorithm = {  	.functionality = i2c_pnx_func,  }; -#ifdef CONFIG_PM -static int i2c_pnx_controller_suspend(struct platform_device *pdev, -				      pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int i2c_pnx_controller_suspend(struct device *dev)  { -	struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); +	struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev); -	/* FIXME: shouldn't this be clk_disable? */ -	clk_enable(alg_data->clk); +	clk_disable(alg_data->clk);  	return 0;  } -static int i2c_pnx_controller_resume(struct platform_device *pdev) +static int i2c_pnx_controller_resume(struct device *dev)  { -	struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); +	struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev);  	return clk_enable(alg_data->clk);  } + +static SIMPLE_DEV_PM_OPS(i2c_pnx_pm, +			 i2c_pnx_controller_suspend, i2c_pnx_controller_resume); +#define PNX_I2C_PM	(&i2c_pnx_pm)  #else -#define i2c_pnx_controller_suspend	NULL -#define i2c_pnx_controller_resume	NULL +#define PNX_I2C_PM	NULL  #endif -static int __devinit i2c_pnx_probe(struct platform_device *pdev) +static int i2c_pnx_probe(struct platform_device *pdev)  {  	unsigned long tmp;  	int ret = 0;  	struct i2c_pnx_algo_data *alg_data;  	unsigned long freq; -	struct i2c_pnx_data *i2c_pnx = pdev->dev.platform_data; +	struct resource *res; +	u32 speed = I2C_PNX_SPEED_KHZ_DEFAULT * 1000; -	if (!i2c_pnx || !i2c_pnx->name) { -		dev_err(&pdev->dev, "%s: no platform data supplied\n", -		       __func__); -		ret = -EINVAL; -		goto out; -	} - -	alg_data = kzalloc(sizeof(*alg_data), GFP_KERNEL); -	if (!alg_data) { -		ret = -ENOMEM; -		goto err_kzalloc; -	} +	alg_data = devm_kzalloc(&pdev->dev, sizeof(*alg_data), GFP_KERNEL); +	if (!alg_data) +		return -ENOMEM;  	platform_set_drvdata(pdev, alg_data); -	strlcpy(alg_data->adapter.name, i2c_pnx->name, -		sizeof(alg_data->adapter.name));  	alg_data->adapter.dev.parent = &pdev->dev;  	alg_data->adapter.algo = &pnx_algorithm;  	alg_data->adapter.algo_data = alg_data;  	alg_data->adapter.nr = pdev->id; -	alg_data->i2c_pnx = i2c_pnx; -	alg_data->clk = clk_get(&pdev->dev, NULL); -	if (IS_ERR(alg_data->clk)) { -		ret = PTR_ERR(alg_data->clk); -		goto out_drvdata; +	alg_data->timeout = I2C_PNX_TIMEOUT_DEFAULT; +#ifdef CONFIG_OF +	alg_data->adapter.dev.of_node = of_node_get(pdev->dev.of_node); +	if (pdev->dev.of_node) { +		of_property_read_u32(pdev->dev.of_node, "clock-frequency", +				     &speed); +		/* +		 * At this point, it is planned to add an OF timeout property. +		 * As soon as there is a consensus about how to call and handle +		 * this, sth. like the following can be put here: +		 * +		 * of_property_read_u32(pdev->dev.of_node, "timeout", +		 *                      &alg_data->timeout); +		 */  	} +#endif +	alg_data->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(alg_data->clk)) +		return PTR_ERR(alg_data->clk);  	init_timer(&alg_data->mif.timer);  	alg_data->mif.timer.function = i2c_pnx_timeout;  	alg_data->mif.timer.data = (unsigned long)alg_data; -	/* Register I/O resource */ -	if (!request_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE, -				pdev->name)) { -		dev_err(&pdev->dev, -		       "I/O region 0x%08x for I2C already in use.\n", -		       i2c_pnx->base); -		ret = -ENODEV; -		goto out_clkget; -	} +	snprintf(alg_data->adapter.name, sizeof(alg_data->adapter.name), +		 "%s", pdev->name); -	alg_data->ioaddr = ioremap(i2c_pnx->base, I2C_PNX_REGION_SIZE); -	if (!alg_data->ioaddr) { -		dev_err(&pdev->dev, "Couldn't ioremap I2C I/O region\n"); -		ret = -ENOMEM; -		goto out_release; -	} +	/* Register I/O resource */ +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	alg_data->ioaddr = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(alg_data->ioaddr)) +		return PTR_ERR(alg_data->ioaddr);  	ret = clk_enable(alg_data->clk);  	if (ret) -		goto out_unmap; +		return ret;  	freq = clk_get_rate(alg_data->clk); @@ -638,21 +689,27 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)  	 * the deglitching filter length.  	 */ -	tmp = ((freq / 1000) / I2C_PNX_SPEED_KHZ) / 2 - 2; +	tmp = (freq / speed) / 2 - 2;  	if (tmp > 0x3FF)  		tmp = 0x3FF;  	iowrite32(tmp, I2C_REG_CKH(alg_data));  	iowrite32(tmp, I2C_REG_CKL(alg_data));  	iowrite32(mcntrl_reset, I2C_REG_CTL(alg_data)); -	if (wait_reset(I2C_PNX_TIMEOUT, alg_data)) { +	if (wait_reset(alg_data)) {  		ret = -ENODEV;  		goto out_clock;  	}  	init_completion(&alg_data->mif.complete); -	ret = request_irq(i2c_pnx->irq, i2c_pnx_interrupt, -			0, pdev->name, alg_data); +	alg_data->irq = platform_get_irq(pdev, 0); +	if (alg_data->irq < 0) { +		dev_err(&pdev->dev, "Failed to get IRQ from platform resource\n"); +		ret = alg_data->irq; +		goto out_clock; +	} +	ret = devm_request_irq(&pdev->dev, alg_data->irq, i2c_pnx_interrupt, +			       0, pdev->name, alg_data);  	if (ret)  		goto out_clock; @@ -660,58 +717,46 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)  	ret = i2c_add_numbered_adapter(&alg_data->adapter);  	if (ret < 0) {  		dev_err(&pdev->dev, "I2C: Failed to add bus\n"); -		goto out_irq; +		goto out_clock;  	}  	dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n", -	       alg_data->adapter.name, i2c_pnx->base, i2c_pnx->irq); +		alg_data->adapter.name, res->start, alg_data->irq);  	return 0; -out_irq: -	free_irq(i2c_pnx->irq, alg_data);  out_clock:  	clk_disable(alg_data->clk); -out_unmap: -	iounmap(alg_data->ioaddr); -out_release: -	release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE); -out_clkget: -	clk_put(alg_data->clk); -out_drvdata: -	kfree(alg_data); -err_kzalloc: -	platform_set_drvdata(pdev, NULL); -out:  	return ret;  } -static int __devexit i2c_pnx_remove(struct platform_device *pdev) +static int i2c_pnx_remove(struct platform_device *pdev)  {  	struct i2c_pnx_algo_data *alg_data = platform_get_drvdata(pdev); -	struct i2c_pnx_data *i2c_pnx = alg_data->i2c_pnx; -	free_irq(i2c_pnx->irq, alg_data);  	i2c_del_adapter(&alg_data->adapter);  	clk_disable(alg_data->clk); -	iounmap(alg_data->ioaddr); -	release_mem_region(i2c_pnx->base, I2C_PNX_REGION_SIZE); -	clk_put(alg_data->clk); -	kfree(alg_data); -	platform_set_drvdata(pdev, NULL);  	return 0;  } +#ifdef CONFIG_OF +static const struct of_device_id i2c_pnx_of_match[] = { +	{ .compatible = "nxp,pnx-i2c" }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, i2c_pnx_of_match); +#endif +  static struct platform_driver i2c_pnx_driver = {  	.driver = {  		.name = "pnx-i2c",  		.owner = THIS_MODULE, +		.of_match_table = of_match_ptr(i2c_pnx_of_match), +		.pm = PNX_I2C_PM,  	},  	.probe = i2c_pnx_probe, -	.remove = __devexit_p(i2c_pnx_remove), -	.suspend = i2c_pnx_controller_suspend, -	.resume = i2c_pnx_controller_resume, +	.remove = i2c_pnx_remove,  };  static int __init i2c_adap_pnx_init(void)  | 
