RE: [Patch v1] i2c: imx7ulp: add i.MX7ULP i2c controller bus driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



 From: Uwe Kleine-König <mailto:u.kleine-koenig@xxxxxxxxxxxxxx>  Sent: Tuesday, March 15, 2016 6:14 PM
> To: Pan Gao <pandy.gao@xxxxxxx>
> Cc: wsa@xxxxxxxxxxxxx; linux-i2c@xxxxxxxxxxxxxxx; Frank Li
> <frank.li@xxxxxxx>; Fugang Duan <fugang.duan@xxxxxxx>
> Subject: Re: [Patch v1] i2c: imx7ulp: add i.MX7ULP i2c controller bus driver
> 
> On Tue, Mar 15, 2016 at 05:51:22PM +0800, Gao Pan wrote:
> > Add i.MX7ULP i2c bus driver which can continue operating in stop modes
> > provided an appropriate clock is available.
> >
> > It is also designed for low CPU overhead with DMA offloading of FIFO
> > register accesses.
> >
> > Signed-off-by: Gao Pan <pandy.gao@xxxxxxx>
> > ---
> >  drivers/i2c/busses/Kconfig       |  10 +
> >  drivers/i2c/busses/Makefile      |   1 +
> >  drivers/i2c/busses/i2c-imx7ulp.c | 810
> > +++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 821 insertions(+)
> >
> > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> > index 0299dfa..9d3fee0 100644
> > --- a/drivers/i2c/busses/Kconfig
> > +++ b/drivers/i2c/busses/Kconfig
> > @@ -594,6 +594,16 @@ config I2C_IMX
> >  	  This driver can also be built as a module.  If so, the module
> >  	  will be called i2c-imx.
> >
> > +config I2C_IMX7ULP
> > +	tristate "IMX7ULP I2C interface"
> > +	depends on ARCH_MXC
> Is there a more specific symbol for i.MX7ULP than ARCH_MXC?
> 
> I'd suggest to use:
> 
> 	depends on ARCH_MXC || COMPILE_TEST
> 
> I'm a bit irritated that this is not a dt driver.

Thanks, I sent V1  for significant comments to optimize this patch.  I will send V2 after validation.

> > +	help
> > +	  Say Y here if you want to use the IIC bus controller on
> > +	  the Freescale i.MX7ULP processors.
> > +
> > +	  This driver can also be built as a module.  If so, the module
> > +	  will be called i2c-imx7ulp.
> > +
> >  config I2C_IOP3XX
> >  	tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface"
> >  	depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX ||
> ARCH_IOP13XX
> > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> > index 37f2819..ef8607b 100644
> > --- a/drivers/i2c/busses/Makefile
> > +++ b/drivers/i2c/busses/Makefile
> > @@ -56,6 +56,7 @@ obj-$(CONFIG_I2C_HIX5HD2)	+= i2c-hix5hd2.o
> >  obj-$(CONFIG_I2C_IBM_IIC)	+= i2c-ibm_iic.o
> >  obj-$(CONFIG_I2C_IMG)		+= i2c-img-scb.o
> >  obj-$(CONFIG_I2C_IMX)		+= i2c-imx.o
> > +obj-$(CONFIG_I2C_IMX7ULP)	+= i2c-imx7ulp.o
> >  obj-$(CONFIG_I2C_IOP3XX)	+= i2c-iop3xx.o
> >  obj-$(CONFIG_I2C_JZ4780)	+= i2c-jz4780.o
> >  obj-$(CONFIG_I2C_KEMPLD)	+= i2c-kempld.o
> > diff --git a/drivers/i2c/busses/i2c-imx7ulp.c
> > b/drivers/i2c/busses/i2c-imx7ulp.c
> > new file mode 100644
> > index 0000000..bf4b1e8
> > --- /dev/null
> > +++ b/drivers/i2c/busses/i2c-imx7ulp.c
> > @@ -0,0 +1,810 @@
> > +/*
> > + * This is i.MX7ULP low power i2c controller driver.
> > + *
> > + * Copyright 2016 Freescale Semiconductor, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version 2
> > + * of the License, or (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#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>
> > +#include <linux/platform_data/i2c-imx.h>
> 
> You don't make use of this one.

Yes, Thanks.

> > +#include <linux/platform_device.h>
> > +#include <linux/sched.h>
> > +#include <linux/slab.h>
> > +
> > +/* This will be the driver name the kernel reports */ #define
> > +DRIVER_NAME "imx7ulp-i2c"
> > +
> > +/* IMX I2C registers */
> > +#define	IMX7ULP_I2C_PARAM	0x04	/* i2c RX/TX FIFO size */
> > +#define	IMX7ULP_I2C_MCR		0x10	/* i2c contrl register */
> > +#define	IMX7ULP_I2C_MSR		0x14	/* i2c status register */
> > +#define	IMX7ULP_I2C_MIER	0x18	/* i2c interrupt enable */
> > +#define	IMX7ULP_I2C_MCFGR0	0x20	/* i2c master configuration */
> > +#define	IMX7ULP_I2C_MCFGR1	0x24	/* i2c master configuration */
> > +#define	IMX7ULP_I2C_MCFGR2	0x28	/* i2c master configuration */
> > +#define	IMX7ULP_I2C_MCFGR3	0x2C	/* i2c master configuration */
> > +#define	IMX7ULP_I2C_MCCR0	0x48	/* i2c master clk configuration
> */
> > +#define	IMX7ULP_I2C_MCCR1	0x50	/* i2c master clk configuration
> */
> > +#define	IMX7ULP_I2C_MFCR	0x58	/* i2c master FIFO control */
> > +#define	IMX7ULP_I2C_MFSR	0x5C	/* i2c master FIFO status */
> > +#define	IMX7ULP_I2C_MTDR	0x60	/* i2c master TX data register
> */
> > +#define	IMX7ULP_I2C_MRDR	0x70	/* i2c master RX data register
> */
> > +
> > +
> > +/* i2c command data */
> > +#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
> > +
> > +
> > +/* Bits of I2C controller registers */
> > +#define	MCR_MEN		0X001
> > +#define	MCR_RST		0X002
> > +#define	MCR_DOZEN	0X004
> > +#define	MCR_DBGEN	0X008
> > +#define	MCR_RTF		0X100
> > +#define	MCR_RRF		0X200
> > +
> > +/* Bits of I2C status registers */
> > +#define	MSR_TDF		(1 << 0)
> > +#define	MSR_RDF		(1 << 1)
> > +#define	MSR_NDF		(1 << 10)
> > +#define	MSR_ALF		(1 << 11)
> > +#define	MSR_MBF		(1 << 24)
> > +#define	MSR_BBF		(1 << 25)
> > +
> > +/* Bits of I2C interrupt enable registers */
> > +#define	MIER_TDIE	0X0001
> > +#define	MIER_RDIE	0X0002
> > +#define	MIER_NDIE	0X0400
> > +
> > +/* Bits of I2C MCFGR1 registers */
> > +#define	MCFGR1_IGNACK	0X0200
> > +
> > +#define	FILTSDA_SHIFT	24
> > +#define	FILTSCL_SHIFT	16
> > +#define	DATAVD_SHIFT	24
> > +#define	SETHOLD_SHIFT	16
> > +#define	CLKHI_SHIFT	8
> > +
> > +#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
> > +
> > +
> > +enum imx7ulp_i2c_mode {
> > +	STANDARD,	/* 100+Kbps */
> > +	FAST,		/* 400+Kbps */
> > +	FAST_PLUS,	/* 1.0+Mbps */
> > +	ULTRA_FAST,	/* 5.0+Mbps */
> > +	HS,		/* 3.4+Mbps */
> > +};
> > +
> > +enum imx7ulp_i2c_pincfg {
> > +	TWO_PIN_OD,	/* 2-pin open drain mode */
> > +	TWO_PIN_OO,	/* 2-pin output only mode (utra-fast mode) */
> > +	TWO_PIN_PP,	/* 2-pin push-pull mode */
> > +	FOUR_PIN_PP,	/* 4-pin push-pull mode */
> > +	TWO_PIN_OD_SS,	/* 2-pin open drain mode with separate slave
> */
> > +	TWO_PIN_OO_SS,	/* 2-pin output only mode with separate slave
> */
> > +	TWO_PIN_PP_SS,	/* 2-pin push-pull mode with separate slave */
> > +	FOUR_PIN_PP_IO,	/* 4-pin push-pull mode (inverted output) */
> > +};
> > +
> > +struct imx7ulp_i2c_modecfg {
> > +	u8	prescale;
> > +	u8	filtscl;
> > +	u8	filtsda;
> > +	u8	sethold;
> > +	u8	clklo;
> > +	u8	clkhi;
> > +	u8	datavd;
> > +};
> > +
> > +struct imx7ulp_i2c_struct {
> > +	struct i2c_adapter	adapter;
> > +	struct clk		*bus_clk;
> > +	struct clk		*per_clk;
> > +	void __iomem		*base;
> > +	wait_queue_head_t	queue;
> > +	unsigned long		msr;
> > +	__u8			*rx_buf;
> > +	__u8			*tx_buf;
> > +	unsigned int		rx_water;
> > +	unsigned int		tx_water;
> > +	unsigned int		msg_len;
> > +	unsigned int		rx_len;
> > +	unsigned int		tx_len;
> > +	unsigned int		msg_done;
> > +	unsigned int		block_data;
> > +	unsigned int		rxfifo_size;
> > +	unsigned int		txfifo_size;
> > +	unsigned int		bitrate;
> > +	enum imx7ulp_i2c_mode	mode;
> > +};
> > +
> > +static inline void imx7ulp_i2c_write_reg(unsigned int val,
> > +		struct imx7ulp_i2c_struct *i2c_imx7ulp, unsigned int reg) {
> > +	writel(val, i2c_imx7ulp->base + reg); }
> > +
> > +static inline unsigned int imx7ulp_i2c_read_reg(
> > +		struct imx7ulp_i2c_struct *i2c_imx7ulp, unsigned int reg) {
> > +	return readl(i2c_imx7ulp->base + reg); }
> > +
> > +static int i2c_imx7ulp_bus_busy(
> > +		struct imx7ulp_i2c_struct *i2c_imx7ulp, int for_busy) {
> > +	unsigned long orig_jiffies = jiffies;
> > +	unsigned int temp;
> > +
> > +	dev_dbg(&i2c_imx7ulp->adapter.dev, "<%s>\n", __func__);
> > +
> > +	while (1) {
> > +		temp = imx7ulp_i2c_read_reg(i2c_imx7ulp,
> IMX7ULP_I2C_MSR);
> > +
> > +		/* check for arbitration lost, clear if set */
> > +		if (temp & MSR_ALF) {
> > +			imx7ulp_i2c_write_reg(temp, i2c_imx7ulp,
> IMX7ULP_I2C_MSR);
> > +			return -EAGAIN;
> > +		}
> > +
> > +		if (for_busy && (temp & MSR_BBF))
> > +			break;
> > +		if (!for_busy && !(temp & MSR_BBF))
> > +			break;
> > +		if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
> > +			dev_dbg(&i2c_imx7ulp->adapter.dev,
> > +				"<%s> I2C bus is busy\n", __func__);
> > +			return -ETIMEDOUT;
> > +		}
> > +		schedule();
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void i2c_imx7ulp_set_mode(struct imx7ulp_i2c_struct
> > +*i2c_imx7ulp) {
> > +	enum imx7ulp_i2c_mode mode;
> > +	unsigned int bitrate = i2c_imx7ulp->bitrate;
> > +
> > +	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;
> > +
> > +	i2c_imx7ulp->mode = mode;
> > +
> > +	dev_dbg(&i2c_imx7ulp->adapter.dev, "I2C MODE = %d\n", mode); }
> > +
> > +/* CLKLO = I2C_CLK_RATIO * CLKHI, SETHOLD = CLKHI, DATAVD = CLKHI/2
> > +*/ static void i2c_imx7ulp_set_bitrate(struct imx7ulp_i2c_struct
> > +*i2c_imx7ulp) {
> > +	unsigned int per_clk_rate;
> > +	unsigned int clk_high, clk_low, clk_cycle;
> > +	unsigned int temp;
> > +	struct imx7ulp_i2c_modecfg modecfg;
> > +
> > +	per_clk_rate = clk_get_rate(i2c_imx7ulp->per_clk);
> > +
> > +	/* filtsda/filtscl = 0 in high speed mode */
> > +	if (i2c_imx7ulp->mode == HS || i2c_imx7ulp->mode == ULTRA_FAST)
> > +		modecfg.filtscl = modecfg.filtsda = 0;
> > +	else
> > +		modecfg.filtscl = modecfg.filtsda = 2;
> > +
> > +	clk_cycle = per_clk_rate / (2 * i2c_imx7ulp->bitrate) - 3
> > +		     - (modecfg.filtscl >> 1);
> > +	clk_high = (clk_cycle + I2C_CLK_RATIO) / (I2C_CLK_RATIO + 1);
> > +	clk_low = clk_cycle - clk_high;
> > +
> > +	modecfg.prescale = 1;
> > +	modecfg.sethold = clk_high;
> > +	modecfg.clklo = clk_low;
> > +	modecfg.clkhi = clk_high;
> > +	modecfg.datavd = clk_high >> 1;
> > +
> > +	temp = modecfg.prescale;
> > +	imx7ulp_i2c_write_reg(temp, i2c_imx7ulp, IMX7ULP_I2C_MCFGR1);
> > +
> > +	temp = modecfg.filtscl << FILTSCL_SHIFT |
> > +				modecfg.filtsda << FILTSDA_SHIFT;
> > +	imx7ulp_i2c_write_reg(temp, i2c_imx7ulp, IMX7ULP_I2C_MCFGR2);
> > +
> > +	temp = modecfg.datavd << DATAVD_SHIFT |
> > +				modecfg.sethold << SETHOLD_SHIFT;
> > +	temp |= modecfg.clkhi << CLKHI_SHIFT | modecfg.clklo;
> > +
> > +	/* IMX7ULP_I2C_MCCR1 used for HS mode */
> > +	if (i2c_imx7ulp->mode == HS)
> > +		imx7ulp_i2c_write_reg(temp, i2c_imx7ulp,
> IMX7ULP_I2C_MCCR1);
> > +	else
> > +		imx7ulp_i2c_write_reg(temp, i2c_imx7ulp,
> IMX7ULP_I2C_MCCR0); }
> > +
> > +static void i2c_imx7ulp_set_pincfg(struct imx7ulp_i2c_struct
> > +*i2c_imx7ulp) {
> > +	unsigned int temp = 0;
> > +	enum imx7ulp_i2c_pincfg pincfg;
> > +	enum imx7ulp_i2c_mode mode = i2c_imx7ulp->mode;
> > +
> > +	if (mode == ULTRA_FAST)
> > +		pincfg = TWO_PIN_OO;
> > +	else
> > +		pincfg = TWO_PIN_OD;
> > +
> > +	temp = imx7ulp_i2c_read_reg(i2c_imx7ulp, IMX7ULP_I2C_MCFGR1);
> > +	temp |= pincfg << 24;
> > +	imx7ulp_i2c_write_reg(temp, i2c_imx7ulp, IMX7ULP_I2C_MCFGR1); }
> > +
> > +static int i2c_imx7ulp_tx_addr(struct imx7ulp_i2c_struct *i2c_imx7ulp,
> > +						struct i2c_msg *msgs, u8 read)
> > +{
> > +	unsigned int temp;
> > +
> > +	/* 10 bit addressing(B1:11110A9A8R/W, B0:A7-A0)*/
> > +	if (msgs->flags & I2C_M_TEN) {
> > +		temp = (msgs->addr & 0x300) >> 7;
> > +		temp |= read | (GEN_START << 8);
> > +		imx7ulp_i2c_write_reg(temp, i2c_imx7ulp,
> IMX7ULP_I2C_MTDR);
> > +
> > +		temp = msgs->addr & 0xff;
> > +		imx7ulp_i2c_write_reg(temp, i2c_imx7ulp,
> IMX7ULP_I2C_MTDR);
> > +	} else {
> > +		temp = (msgs->addr << 1 | read) | (GEN_START << 8);
> > +		imx7ulp_i2c_write_reg(temp, i2c_imx7ulp,
> IMX7ULP_I2C_MTDR);
> > +	}
> > +
> > +	return i2c_imx7ulp_bus_busy(i2c_imx7ulp, 1); }
> > +
> > +static int i2c_imx7ulp_start(struct imx7ulp_i2c_struct *i2c_imx7ulp)
> > +{
> > +	int ret;
> > +	unsigned int temp;
> > +
> > +	dev_dbg(&i2c_imx7ulp->adapter.dev, "<%s>\n", __func__);
> > +
> > +	ret = clk_prepare_enable(i2c_imx7ulp->bus_clk);
> > +	if (ret) {
> > +		dev_err(&i2c_imx7ulp->adapter.dev,
> > +			"can't enable I2C bus clock, ret=%d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = clk_prepare_enable(i2c_imx7ulp->per_clk);
> > +	if (ret) {
> > +		dev_err(&i2c_imx7ulp->adapter.dev,
> > +			"can't enable I2C peripheral clock, ret=%d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	/* mater logic register reset */
> 
> master?

Yes, thanks.

> > +	temp = imx7ulp_i2c_read_reg(i2c_imx7ulp, IMX7ULP_I2C_MCR);
> > +	temp |= MCR_RST;
> > +	imx7ulp_i2c_write_reg(temp, i2c_imx7ulp, IMX7ULP_I2C_MCR);
> > [...]
> > +static int i2c_imx7ulp_xfer(struct i2c_adapter *adapter,
> > +						struct i2c_msg *msgs, int num)
> > +{
> > +	unsigned int i;
> > +	int result;
> > +	struct imx7ulp_i2c_struct *i2c_imx7ulp = i2c_get_adapdata(adapter);
> > +
> > +#ifdef CONFIG_I2C_DEBUG_BUS
> > +	unsigned int temp;
> > +#endif
> 
> This can be local to the for loop below

Thanks.
 
> > +
> > +	dev_dbg(&i2c_imx7ulp->adapter.dev, "<%s>\n", __func__);
> > +
> > +	result = i2c_imx7ulp_start(i2c_imx7ulp);
> > +	if (result)
> > +		return result;
> > +
> > +	/* read/write data */
> > +	for (i = 0; i < num; i++) {
> > +		dev_dbg(&i2c_imx7ulp->adapter.dev,
> > +			"<%s> transfer message: %d\n", __func__, i);
> > +
> > +#ifdef CONFIG_I2C_DEBUG_BUS
> > +		temp = imx7ulp_i2c_read_reg(i2c_imx7ulp,
> IMX7ULP_I2C_MCR);
> > +		dev_dbg(&i2c_imx7ulp->adapter.dev,
> > +			"<%s> CONTROL: MEN=%d, RST=%d, DOZEN=%d,
> DBGEN=%d, RTF=%d, RRF=%d\n",
> > +			__func__,
> > +			(temp & MCR_MEN ? 1 : 0), (temp & MCR_RST ? 1 : 0),
> > +			(temp & MCR_DOZEN ? 1 : 0), (temp & MCR_DBGEN  ?
> 1 : 0),
> > +			(temp & MCR_RTF ? 1 : 0), (temp & MCR_RRF ? 1 : 0));
> 
> Maybe it's easier and more useful to just print out the register value instead of
> decoding it here?

Yes,  you are right. Thanks.  

> > +#endif
> > +
> > +		if (msgs[i].flags & I2C_M_RD)
> > +			result = i2c_imx7ulp_read(i2c_imx7ulp, &msgs[i]);
> > +		else
> > +			result = i2c_imx7ulp_write(i2c_imx7ulp, &msgs[i]);
> > +		if (result)
> > +			goto fail0;
> > +	}
> > [...]
> > +static int i2c_imx7ulp_probe(struct platform_device *pdev) {
> > +	struct imx7ulp_i2c_struct *i2c_imx7ulp;
> > +	struct resource *res;
> > +	void __iomem *base;
> > +	int irq, ret;
> > +	unsigned int temp;
> > +
> > +	irq = platform_get_irq(pdev, 0);
> > +	if (irq < 0) {
> > +		dev_err(&pdev->dev, "can't get irq number\n");
> > +		return irq;
> > +	}
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	base = devm_ioremap_resource(&pdev->dev, res);
> > +	if (IS_ERR(base))
> > +		return PTR_ERR(base);
> > +
> > +	i2c_imx7ulp = devm_kzalloc(&pdev->dev,
> > +				sizeof(*i2c_imx7ulp), GFP_KERNEL);
> > +	if (!i2c_imx7ulp)
> > +		return -ENOMEM;
> > +
> > +	/* Setup i2c_imx7ulp driver structure */
> > +	strlcpy(i2c_imx7ulp->adapter.name, pdev->name,
> > +				sizeof(i2c_imx7ulp->adapter.name));
> > +	i2c_imx7ulp->adapter.owner	= THIS_MODULE;
> > +	i2c_imx7ulp->adapter.algo	= &i2c_imx7ulp_algo;
> > +	i2c_imx7ulp->adapter.dev.parent	= &pdev->dev;
> > +	i2c_imx7ulp->adapter.nr		= pdev->id;
> > +	i2c_imx7ulp->base		= base;
> > +	i2c_imx7ulp->adapter.dev.of_node = pdev->dev.of_node;
> > +
> > +	/* Get I2C bus clock */
> > +	i2c_imx7ulp->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
> > +	if (IS_ERR(i2c_imx7ulp->bus_clk)) {
> > +		dev_err(&pdev->dev, "can't get I2C bus clock\n");
> > +		return PTR_ERR(i2c_imx7ulp->bus_clk);
> > +	}
> > +
> > +	/* Get I2C peripheral clock */
> > +	i2c_imx7ulp->per_clk = devm_clk_get(&pdev->dev, "per_clk");
> > +	if (IS_ERR(i2c_imx7ulp->per_clk)) {
> > +		dev_err(&pdev->dev, "can't get I2C peripheral clock\n");
> > +		return PTR_ERR(i2c_imx7ulp->per_clk);
> > +	}
> > +
> > +	ret = clk_prepare_enable(i2c_imx7ulp->bus_clk);
> > +	if (ret) {
> > +		dev_err(&pdev->dev,
> > +			"can't enable I2C bus clock, ret=%d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	/* Request IRQ */
> > +	ret = devm_request_irq(&pdev->dev, irq, i2c_imx7ulp_isr, 0,
> > +				pdev->name, i2c_imx7ulp);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "can't claim irq %d\n", irq);
> > +		goto clk_disable;
> > +	}
> 
> I think the irq should be requested later unless you ensure the irq cannot trigger
> until all the initializing below is complete. (And even then it might be cleaner.)

Thanks,  will fix it the next version.

> > +
> > +	/* Init queue */
> > +	init_waitqueue_head(&i2c_imx7ulp->queue);
> > +
> > +	/* Set up adapter data */
> > +	i2c_set_adapdata(&i2c_imx7ulp->adapter, i2c_imx7ulp);
> > +
> > +	/* Set up platform driver data */
> > +	platform_set_drvdata(pdev, i2c_imx7ulp);
> > +
> > +	/* Read i2c fifosize parameter, unit: words */
> > +	temp = imx7ulp_i2c_read_reg(i2c_imx7ulp, IMX7ULP_I2C_PARAM);
> > +	i2c_imx7ulp->txfifo_size = 1 <<  (temp & 0X000F);
> > +	i2c_imx7ulp->rxfifo_size = 1 <<  (temp & 0X0F00);
> > +
> > +	ret = of_property_read_u32(pdev->dev.of_node,
> > +				   "clock-frequency", &i2c_imx7ulp->bitrate);
> > +	if (ret)
> > +		i2c_imx7ulp->bitrate = LPI2C_DEFAULT_RATE;
> > +
> > +	/*i2c imx7ulp basic config */
> > +	i2c_imx7ulp_set_mode(i2c_imx7ulp);
> > +	i2c_imx7ulp_set_bitrate(i2c_imx7ulp);
> > +	i2c_imx7ulp_set_pincfg(i2c_imx7ulp);
> > +
> > +	/* Add I2C adapter */
> > +	ret = i2c_add_numbered_adapter(&i2c_imx7ulp->adapter);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "registration failed\n");
> > +		goto clk_disable;
> > +	}
> > +
> > +	dev_dbg(&i2c_imx7ulp->adapter.dev, "claimed irq %d\n", irq);
> > +	dev_dbg(&i2c_imx7ulp->adapter.dev, "device resources: %pR\n", res);
> > +	dev_dbg(&i2c_imx7ulp->adapter.dev, "adapter name: \"%s\"\n",
> > +		i2c_imx7ulp->adapter.name);
> > +	dev_info(&i2c_imx7ulp->adapter.dev, "IMX I2C adapter registered\n");
> > +
> > +clk_disable:
> > +	clk_disable_unprepare(i2c_imx7ulp->bus_clk);
> > +	return ret;
> > +}
> > +
> > +static int i2c_imx7ulp_remove(struct platform_device *pdev) {
> > +	struct imx7ulp_i2c_struct *i2c_imx7ulp = platform_get_drvdata(pdev);
> > +
> > +	i2c_del_adapter(&i2c_imx7ulp->adapter);
> > +	clk_disable_unprepare(i2c_imx7ulp->bus_clk);
> > +	clk_disable_unprepare(i2c_imx7ulp->per_clk);
> 
> After probe the busclk is unprepared already. Do these call balance correctly
> here?

Thanks, these calls are unnecessary here, will fix it the next version.

Best  Regards
Gao Pan
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux